prisma-generator-express 1.24.0 → 1.25.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/dist/bin.d.ts +1 -1
- package/dist/bin.js +1 -1
- package/dist/bin.js.map +1 -1
- package/dist/generators/generateRouter.js +5 -5
- package/dist/generators/generateUnifiedDocs.js +73 -64
- package/dist/generators/generateUnifiedDocs.js.map +1 -1
- package/dist/generators/generateUnifiedHandler.js +11 -6
- package/dist/generators/generateUnifiedHandler.js.map +1 -1
- package/dist/generators/generateUnifiedScalarUI.js +3 -3
- package/dist/index.js +23 -23
- package/dist/index.js.map +1 -1
- package/dist/utils/writeFileSafely.js +0 -6
- package/dist/utils/writeFileSafely.js.map +1 -1
- package/package.json +1 -1
- package/src/bin.ts +1 -1
- package/src/client/encodeQueryParams.ts +1 -1
- package/src/copy/buildModelOpenApi.ts +83 -19
- package/src/copy/parseQueryParams.ts +1 -1
- package/src/generators/generateImportPrismaStatement.ts +1 -1
- package/src/generators/generateRouter.ts +5 -5
- package/src/generators/generateUnifiedDocs.ts +74 -64
- package/src/generators/generateUnifiedHandler.ts +12 -7
- package/src/generators/generateUnifiedScalarUI.ts +3 -3
- package/src/index.ts +9 -9
- package/src/utils/writeFileSafely.ts +1 -9
package/dist/bin.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import '.';
|
|
2
|
+
import './index.js';
|
package/dist/bin.js
CHANGED
package/dist/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";;;AACA,
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";;;AACA,sBAAmB"}
|
|
@@ -44,10 +44,10 @@ import {
|
|
|
44
44
|
${modelName}Aggregate,
|
|
45
45
|
${modelName}Count,
|
|
46
46
|
${modelName}GroupBy
|
|
47
|
-
} from './${modelName}Handlers'
|
|
48
|
-
import type { RouteConfig } from '../routeConfig'
|
|
49
|
-
import { parseQueryParams } from '../parseQueryParams'
|
|
50
|
-
import { buildModelOpenApi } from '../buildModelOpenApi'
|
|
47
|
+
} from './${modelName}Handlers.js'
|
|
48
|
+
import type { RouteConfig } from '../routeConfig.js'
|
|
49
|
+
import { parseQueryParams } from '../parseQueryParams.js'
|
|
50
|
+
import { buildModelOpenApi } from '../buildModelOpenApi.js'
|
|
51
51
|
|
|
52
52
|
const _env = typeof process !== 'undefined' && process.env ? process.env : {} as Record<string, string | undefined>
|
|
53
53
|
|
|
@@ -127,7 +127,7 @@ export function ${routerFunctionName}(config: RouteConfig = {}) {
|
|
|
127
127
|
if (qbEnabled) {
|
|
128
128
|
const qbConfig = getQueryBuilderConfig(config)
|
|
129
129
|
if (qbConfig) {
|
|
130
|
-
import('../queryBuilder').then(mod => mod.startQueryBuilder(qbConfig)).catch(() => {})
|
|
130
|
+
import('../queryBuilder.js').then(mod => mod.startQueryBuilder(qbConfig)).catch((err) => { if (_env.NODE_ENV !== 'production') console.warn('[query-builder]', err) })
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
|
|
@@ -5,6 +5,9 @@ function generateUnifiedDocs(models) {
|
|
|
5
5
|
const imports = models
|
|
6
6
|
.map((model) => `import { ${model}Docs } from './${model}/${model}Docs'`)
|
|
7
7
|
.join('\n');
|
|
8
|
+
const handlersEntries = models
|
|
9
|
+
.map((model) => ` ${model}: ${model}Docs`)
|
|
10
|
+
.join(',\n');
|
|
8
11
|
return `${imports}
|
|
9
12
|
import { Request, Response } from 'express'
|
|
10
13
|
import type { RouteConfig } from './routeConfig'
|
|
@@ -12,7 +15,7 @@ import type { RouteConfig } from './routeConfig'
|
|
|
12
15
|
const _env = typeof process !== 'undefined' && process.env ? process.env : {} as Record<string, string | undefined>
|
|
13
16
|
|
|
14
17
|
const docsHandlers: Record<string, (config: any) => (req: Request, res: Response) => any> = {
|
|
15
|
-
${
|
|
18
|
+
${handlersEntries}
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
type DocsUI = 'docs' | 'scalar' | 'json' | 'yaml' | 'playground'
|
|
@@ -79,66 +82,72 @@ export function generateCombinedDocs(config: CombinedDocsConfig) {
|
|
|
79
82
|
const basePath = removeTrailingSlash(config.basePath || '/docs')
|
|
80
83
|
const generatedAt = new Date().toISOString()
|
|
81
84
|
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
</
|
|
141
|
-
</
|
|
85
|
+
const modelRows = registeredModels.map((m) => {
|
|
86
|
+
const lower = m.toLowerCase()
|
|
87
|
+
const docsUrl = basePath + '/' + lower
|
|
88
|
+
const scalarUrl = docsUrl + '?ui=scalar'
|
|
89
|
+
const jsonUrl = docsUrl + '?ui=json'
|
|
90
|
+
const yamlUrl = docsUrl + '?ui=yaml'
|
|
91
|
+
const playgroundUrl = docsUrl + '?ui=playground'
|
|
92
|
+
const modelCfg = config.modelConfigs[m]
|
|
93
|
+
const modelPlayground = isPlaygroundAvailable(modelCfg)
|
|
94
|
+
const playgroundLink = modelPlayground
|
|
95
|
+
? ', <a href="' + playgroundUrl + '" class="text-inherit underline">playground</a>'
|
|
96
|
+
: ''
|
|
97
|
+
return '<tr>' +
|
|
98
|
+
'<td class="text-left py-2 px-2 border-b border-gray-300 align-top">' + escapeHtml(m) + '</td>' +
|
|
99
|
+
'<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>' +
|
|
100
|
+
'<td class="text-left py-2 px-2 border-b border-gray-300 align-top">' +
|
|
101
|
+
'<a href="' + scalarUrl + '" class="text-inherit underline">scalar</a>, ' +
|
|
102
|
+
'<a href="' + jsonUrl + '" class="text-inherit underline">json</a>, ' +
|
|
103
|
+
'<a href="' + yamlUrl + '" class="text-inherit underline">yaml</a>' +
|
|
104
|
+
playgroundLink +
|
|
105
|
+
'</td>' +
|
|
106
|
+
'</tr>'
|
|
107
|
+
}).join('')
|
|
108
|
+
|
|
109
|
+
const descriptionHtml = description
|
|
110
|
+
? '<div class="mt-1.5 text-gray-500 text-sm">' + escapeHtml(description) + '</div>'
|
|
111
|
+
: ''
|
|
112
|
+
const versionHtml = version
|
|
113
|
+
? '<div>Version: ' + escapeHtml(version) + '</div>'
|
|
114
|
+
: ''
|
|
115
|
+
|
|
116
|
+
const html =
|
|
117
|
+
'<!DOCTYPE html>' +
|
|
118
|
+
'<html lang="en">' +
|
|
119
|
+
'<head>' +
|
|
120
|
+
'<meta charset="utf-8" />' +
|
|
121
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1" />' +
|
|
122
|
+
'<title>' + escapeHtml(title) + '</title>' +
|
|
123
|
+
'<script src="https://cdn.tailwindcss.com"></' + 'script>' +
|
|
124
|
+
'</head>' +
|
|
125
|
+
'<body class="m-0 bg-white text-gray-900 font-serif leading-normal">' +
|
|
126
|
+
'<div class="max-w-[980px] mx-auto px-7 pt-10 pb-16">' +
|
|
127
|
+
'<div class="border-b-2 border-gray-900 pb-3.5 mb-[18px]">' +
|
|
128
|
+
'<div class="text-[28px] font-bold tracking-wide">' + escapeHtml(title) + '</div>' +
|
|
129
|
+
descriptionHtml +
|
|
130
|
+
'<div class="mt-3 flex gap-x-5 text-[13px] text-gray-500">' +
|
|
131
|
+
versionHtml +
|
|
132
|
+
'<div>Generated: ' + escapeHtml(generatedAt) + '</div>' +
|
|
133
|
+
'</div>' +
|
|
134
|
+
'</div>' +
|
|
135
|
+
'<div class="mt-[22px]">' +
|
|
136
|
+
'<h2 class="m-0 mb-2.5 text-lg border-t border-gray-300 pt-3.5">Models</h2>' +
|
|
137
|
+
'<table class="w-full border-collapse text-[13px]">' +
|
|
138
|
+
'<thead>' +
|
|
139
|
+
'<tr>' +
|
|
140
|
+
'<th class="text-left py-2 px-2 border-b border-gray-300 align-top font-bold">Model</th>' +
|
|
141
|
+
'<th class="text-left py-2 px-2 border-b border-gray-300 align-top font-bold">Documentation</th>' +
|
|
142
|
+
'<th class="text-left py-2 px-2 border-b border-gray-300 align-top font-bold">Views</th>' +
|
|
143
|
+
'</tr>' +
|
|
144
|
+
'</thead>' +
|
|
145
|
+
'<tbody>' + modelRows + '</tbody>' +
|
|
146
|
+
'</table>' +
|
|
147
|
+
'</div>' +
|
|
148
|
+
'</div>' +
|
|
149
|
+
'</body>' +
|
|
150
|
+
'</html>'
|
|
142
151
|
|
|
143
152
|
res.type('html').send(html)
|
|
144
153
|
}
|
|
@@ -161,9 +170,9 @@ export function registerModelDocs(
|
|
|
161
170
|
registeredModels.forEach((model) => {
|
|
162
171
|
const handler = docsHandlers[model]
|
|
163
172
|
const cfg = configs[model] || {}
|
|
164
|
-
const
|
|
165
|
-
console.log(
|
|
166
|
-
app.get(
|
|
173
|
+
const docPath = normalizedBase + '/' + model.toLowerCase()
|
|
174
|
+
console.log(' Registered docs: ' + docPath)
|
|
175
|
+
app.get(docPath, handler(cfg))
|
|
167
176
|
})
|
|
168
177
|
}
|
|
169
178
|
`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateUnifiedDocs.js","sourceRoot":"","sources":["../../src/generators/generateUnifiedDocs.ts"],"names":[],"mappings":";;AAAA,
|
|
1
|
+
{"version":3,"file":"generateUnifiedDocs.js","sourceRoot":"","sources":["../../src/generators/generateUnifiedDocs.ts"],"names":[],"mappings":";;AAAA,kDAiLC;AAjLD,SAAgB,mBAAmB,CAAC,MAAgB;IAClD,MAAM,OAAO,GAAG,MAAM;SACnB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,KAAK,kBAAkB,KAAK,IAAI,KAAK,OAAO,CAAC;SACxE,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,MAAM,eAAe,GAAG,MAAM;SAC3B,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,KAAK,KAAK,MAAM,CAAC;SAC1C,IAAI,CAAC,KAAK,CAAC,CAAA;IAEd,OAAO,GAAG,OAAO;;;;;;;EAOjB,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgKhB,CAAA;AACD,CAAC"}
|
|
@@ -146,6 +146,7 @@ function handleError(error: unknown, next: NextFunction): void {
|
|
|
146
146
|
return
|
|
147
147
|
}
|
|
148
148
|
if (typeof code === 'string' && code.startsWith('P')) {
|
|
149
|
+
console.warn('[prisma-generator-express] Unmapped Prisma error code:', code, (error as any).message || '')
|
|
149
150
|
next(new HttpError(500, 'Database operation failed'))
|
|
150
151
|
return
|
|
151
152
|
}
|
|
@@ -397,6 +398,14 @@ async function countForPagination(
|
|
|
397
398
|
const countShape = shape ? buildCountShape(shape) : undefined
|
|
398
399
|
|
|
399
400
|
if (hasDistinct) {
|
|
401
|
+
if (shape) {
|
|
402
|
+
const countArgs: Record<string, any> = {}
|
|
403
|
+
if (query.where) countArgs.where = query.where
|
|
404
|
+
return countShape
|
|
405
|
+
? await delegate.guard(countShape, caller).count(countArgs)
|
|
406
|
+
: await delegate.count(countArgs)
|
|
407
|
+
}
|
|
408
|
+
|
|
400
409
|
const selectField = distinctFields[0]
|
|
401
410
|
const distinctArgs: Record<string, any> = {
|
|
402
411
|
where: query.where,
|
|
@@ -405,17 +414,13 @@ async function countForPagination(
|
|
|
405
414
|
take: DISTINCT_COUNT_LIMIT + 1,
|
|
406
415
|
}
|
|
407
416
|
|
|
408
|
-
const results =
|
|
409
|
-
? await delegate.guard(shape, caller).findMany(distinctArgs)
|
|
410
|
-
: await delegate.findMany(distinctArgs)
|
|
417
|
+
const results = await delegate.findMany(distinctArgs)
|
|
411
418
|
|
|
412
419
|
if (results.length > DISTINCT_COUNT_LIMIT) {
|
|
413
420
|
console.warn('[prisma-generator-express] Distinct count exceeds ' + DISTINCT_COUNT_LIMIT + ', falling back to approximate total')
|
|
414
421
|
const countArgs: Record<string, any> = {}
|
|
415
422
|
if (query.where) countArgs.where = query.where
|
|
416
|
-
return
|
|
417
|
-
? await delegate.guard(countShape, caller).count(countArgs)
|
|
418
|
-
: await delegate.count(countArgs)
|
|
423
|
+
return await delegate.count(countArgs)
|
|
419
424
|
}
|
|
420
425
|
|
|
421
426
|
return results.length
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateUnifiedHandler.js","sourceRoot":"","sources":["../../src/generators/generateUnifiedHandler.ts"],"names":[],"mappings":";;AAOA,
|
|
1
|
+
{"version":3,"file":"generateUnifiedHandler.js","sourceRoot":"","sources":["../../src/generators/generateUnifiedHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -67,9 +67,9 @@ function generateScalarUIHandler(options) {
|
|
|
67
67
|
fields: idx.fields,
|
|
68
68
|
}));
|
|
69
69
|
return `import { Request, Response } from 'express'
|
|
70
|
-
import { buildModelOpenApi } from '../buildModelOpenApi'
|
|
71
|
-
import type { RouteConfig } from '../routeConfig'
|
|
72
|
-
import { OPERATION_DEFS, isOperationEnabled } from '../operationDefinitions'
|
|
70
|
+
import { buildModelOpenApi } from '../buildModelOpenApi.js'
|
|
71
|
+
import type { RouteConfig } from '../routeConfig.js'
|
|
72
|
+
import { OPERATION_DEFS, isOperationEnabled } from '../operationDefinitions.js'
|
|
73
73
|
|
|
74
74
|
const _env = typeof process !== 'undefined' && process.env ? process.env : {} as Record<string, string | undefined>
|
|
75
75
|
|
package/dist/index.js
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const generator_helper_1 = require("@prisma/generator-helper");
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
4
|
+
const generateUnifiedHandler_js_1 = require("./generators/generateUnifiedHandler.js");
|
|
5
|
+
const generateRouter_js_1 = require("./generators/generateRouter.js");
|
|
6
|
+
const generateUnifiedScalarUI_js_1 = require("./generators/generateUnifiedScalarUI.js");
|
|
7
|
+
const generateUnifiedDocs_js_1 = require("./generators/generateUnifiedDocs.js");
|
|
8
|
+
const generateQueryBuilderHelper_js_1 = require("./generators/generateQueryBuilderHelper.js");
|
|
9
|
+
const generateImportPrismaStatement_js_1 = require("./generators/generateImportPrismaStatement.js");
|
|
10
|
+
const writeFileSafely_js_1 = require("./utils/writeFileSafely.js");
|
|
11
|
+
const copyFiles_js_1 = require("./utils/copyFiles.js");
|
|
12
|
+
const constants_js_1 = require("./constants.js");
|
|
13
13
|
(0, generator_helper_1.generatorHandler)({
|
|
14
14
|
onManifest() {
|
|
15
15
|
return {
|
|
16
16
|
version: require('../package.json').version,
|
|
17
17
|
defaultOutput: '../generated',
|
|
18
|
-
prettyName:
|
|
18
|
+
prettyName: constants_js_1.GENERATOR_NAME,
|
|
19
19
|
};
|
|
20
20
|
},
|
|
21
21
|
async onGenerate(options) {
|
|
22
|
-
const prismaImportStatement = (0,
|
|
22
|
+
const prismaImportStatement = (0, generateImportPrismaStatement_js_1.generateImportPrismaStatement)(options);
|
|
23
23
|
console.log('\n═══ Prisma Generator Express ═══');
|
|
24
|
-
await (0,
|
|
24
|
+
await (0, copyFiles_js_1.copyFiles)(options);
|
|
25
25
|
const modelNames = [];
|
|
26
26
|
for (const model of options.dmmf.datamodel.models) {
|
|
27
27
|
if (model.documentation &&
|
|
@@ -30,9 +30,9 @@ const constants_1 = require("./constants");
|
|
|
30
30
|
continue;
|
|
31
31
|
}
|
|
32
32
|
modelNames.push(model.name);
|
|
33
|
-
const relativeClientPath = (0,
|
|
34
|
-
await (0,
|
|
35
|
-
content: (0,
|
|
33
|
+
const relativeClientPath = (0, generateImportPrismaStatement_js_1.getRelativeClientPath)(options, model.name);
|
|
34
|
+
await (0, writeFileSafely_js_1.writeFileSafely)({
|
|
35
|
+
content: (0, generateUnifiedHandler_js_1.generateUnifiedHandler)({
|
|
36
36
|
model: model,
|
|
37
37
|
prismaImportStatement,
|
|
38
38
|
}),
|
|
@@ -40,8 +40,8 @@ const constants_1 = require("./constants");
|
|
|
40
40
|
model: model,
|
|
41
41
|
operation: 'Handlers',
|
|
42
42
|
});
|
|
43
|
-
await (0,
|
|
44
|
-
content: (0,
|
|
43
|
+
await (0, writeFileSafely_js_1.writeFileSafely)({
|
|
44
|
+
content: (0, generateRouter_js_1.generateRouterFunction)({
|
|
45
45
|
model: model,
|
|
46
46
|
enums: options.dmmf.datamodel.enums,
|
|
47
47
|
relativeClientPath,
|
|
@@ -50,8 +50,8 @@ const constants_1 = require("./constants");
|
|
|
50
50
|
model: model,
|
|
51
51
|
operation: 'Router',
|
|
52
52
|
});
|
|
53
|
-
await (0,
|
|
54
|
-
content: (0,
|
|
53
|
+
await (0, writeFileSafely_js_1.writeFileSafely)({
|
|
54
|
+
content: (0, generateUnifiedScalarUI_js_1.generateScalarUIHandler)({
|
|
55
55
|
model: model,
|
|
56
56
|
enums: options.dmmf.datamodel.enums,
|
|
57
57
|
}),
|
|
@@ -60,13 +60,13 @@ const constants_1 = require("./constants");
|
|
|
60
60
|
operation: 'Docs',
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
|
-
await (0,
|
|
64
|
-
content: (0,
|
|
63
|
+
await (0, writeFileSafely_js_1.writeFileSafely)({
|
|
64
|
+
content: (0, generateUnifiedDocs_js_1.generateUnifiedDocs)(modelNames),
|
|
65
65
|
options,
|
|
66
66
|
operation: 'combinedDocs',
|
|
67
67
|
});
|
|
68
|
-
await (0,
|
|
69
|
-
content: (0,
|
|
68
|
+
await (0, writeFileSafely_js_1.writeFileSafely)({
|
|
69
|
+
content: (0, generateQueryBuilderHelper_js_1.generateQueryBuilderHelper)(options),
|
|
70
70
|
options,
|
|
71
71
|
operation: 'queryBuilder',
|
|
72
72
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAAA,+DAIiC;AACjC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAAA,+DAIiC;AACjC,sFAA+E;AAC/E,sEAAuE;AACvE,wFAAiF;AACjF,gFAAyE;AACzE,8FAAuF;AACvF,oGAGsD;AACtD,mEAA4D;AAC5D,uDAAgD;AAChD,iDAA+C;AAE/C,IAAA,mCAAgB,EAAC;IACf,UAAU;QACR,OAAO;YACL,OAAO,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC,OAAO;YAC3C,aAAa,EAAE,cAAc;YAC7B,UAAU,EAAE,6BAAc;SAC3B,CAAA;IACH,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,OAAyB;QACxC,MAAM,qBAAqB,GAAG,IAAA,gEAA6B,EAAC,OAAO,CAAC,CAAA;QAEpE,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAA;QAEjD,MAAM,IAAA,wBAAS,EAAC,OAAO,CAAC,CAAA;QAExB,MAAM,UAAU,GAAa,EAAE,CAAA;QAE/B,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;YAClD,IACE,KAAK,CAAC,aAAa;gBACnB,KAAK,CAAC,aAAa,CAAC,QAAQ,CAAC,eAAe,CAAC,EAC7C,CAAC;gBACD,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,CAAC,IAAI,kBAAkB,CAAC,CAAA;gBACxD,SAAQ;YACV,CAAC;YAED,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YAE3B,MAAM,kBAAkB,GAAG,IAAA,wDAAqB,EAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;YAErE,MAAM,IAAA,oCAAe,EAAC;gBACpB,OAAO,EAAE,IAAA,kDAAsB,EAAC;oBAC9B,KAAK,EAAE,KAAmB;oBAC1B,qBAAqB;iBACtB,CAAC;gBACF,OAAO;gBACP,KAAK,EAAE,KAAmB;gBAC1B,SAAS,EAAE,UAAU;aACtB,CAAC,CAAA;YAEF,MAAM,IAAA,oCAAe,EAAC;gBACpB,OAAO,EAAE,IAAA,0CAAsB,EAAC;oBAC9B,KAAK,EAAE,KAAmB;oBAC1B,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,KAA6B;oBAC3D,kBAAkB;iBACnB,CAAC;gBACF,OAAO;gBACP,KAAK,EAAE,KAAmB;gBAC1B,SAAS,EAAE,QAAQ;aACpB,CAAC,CAAA;YAEF,MAAM,IAAA,oCAAe,EAAC;gBACpB,OAAO,EAAE,IAAA,oDAAuB,EAAC;oBAC/B,KAAK,EAAE,KAAmB;oBAC1B,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,KAA6B;iBAC5D,CAAC;gBACF,OAAO;gBACP,KAAK,EAAE,KAAmB;gBAC1B,SAAS,EAAE,MAAM;aAClB,CAAC,CAAA;QACJ,CAAC;QAED,MAAM,IAAA,oCAAe,EAAC;YACpB,OAAO,EAAE,IAAA,4CAAmB,EAAC,UAAU,CAAC;YACxC,OAAO;YACP,SAAS,EAAE,cAAc;SAC1B,CAAC,CAAA;QAEF,MAAM,IAAA,oCAAe,EAAC;YACpB,OAAO,EAAE,IAAA,0DAA0B,EAAC,OAAO,CAAC;YAC5C,OAAO;YACP,SAAS,EAAE,cAAc;SAC1B,CAAC,CAAA;QAEF,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAA;QAC5C,OAAO,CAAC,GAAG,CAAC,KAAK,UAAU,CAAC,MAAM,SAAS,CAAC,CAAA;QAC5C,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAA;QAChD,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAA;QAC/C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACjB,CAAC;CACF,CAAC,CAAA"}
|
|
@@ -54,12 +54,6 @@ async function writeFileSafely({ content, options, model, operation, }) {
|
|
|
54
54
|
}
|
|
55
55
|
let filePath;
|
|
56
56
|
switch (operation) {
|
|
57
|
-
case 'cacheConfig':
|
|
58
|
-
filePath = path.join(outputPath, 'cacheConfig.ts');
|
|
59
|
-
break;
|
|
60
|
-
case 'types/inputs':
|
|
61
|
-
filePath = path.join(outputPath, 'types', 'inputs.ts');
|
|
62
|
-
break;
|
|
63
57
|
case 'combinedDocs':
|
|
64
58
|
filePath = path.join(outputPath, 'combinedDocs.ts');
|
|
65
59
|
break;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writeFileSafely.js","sourceRoot":"","sources":["../../src/utils/writeFileSafely.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqBA,
|
|
1
|
+
{"version":3,"file":"writeFileSafely.js","sourceRoot":"","sources":["../../src/utils/writeFileSafely.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqBA,0CAqDC;AAxED,uCAAwB;AACxB,2CAA4B;AAC5B,wDAA+B;AAS/B,IAAI,gBAAqD,CAAA;AAEzD,KAAK,UAAU,kBAAkB;IAC/B,IAAI,gBAAgB,KAAK,SAAS;QAAE,OAAO,gBAAgB,CAAA;IAC3D,gBAAgB,GAAG,MAAM,kBAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;IAC9D,OAAO,gBAAgB,CAAA;AACzB,CAAC;AAEM,KAAK,UAAU,eAAe,CAAC,EACpC,OAAO,EACP,OAAO,EACP,KAAK,EACL,SAAS,GACQ;IACjB,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAA;IAClD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAA;IAC5C,CAAC;IAED,IAAI,QAAgB,CAAA;IAEpB,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,cAAc;YACjB,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAA;YACnD,MAAK;QAEP,KAAK,cAAc;YACjB,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAA;YACnD,MAAK;QAEP;YACE,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,IAAI,KAAK,CAAC,iCAAiC,SAAS,EAAE,CAAC,CAAA;YAC/D,CAAC;YACD,QAAQ,GAAG,IAAI,CAAC,IAAI,CAClB,UAAU,EACV,KAAK,CAAC,IAAI,EACV,GAAG,KAAK,CAAC,IAAI,GAAG,SAAS,KAAK,CAC/B,CAAA;IACL,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IACtC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC5C,CAAC;IAED,IAAI,gBAAwB,CAAA;IAC5B,IAAI,CAAC;QACH,MAAM,eAAe,GAAG,MAAM,kBAAkB,EAAE,CAAA;QAClD,gBAAgB,GAAG,MAAM,kBAAQ,CAAC,MAAM,CAAC,OAAO,EAAE;YAChD,GAAG,eAAe;YAClB,MAAM,EAAE,YAAY;SACrB,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CACV,sCAAsC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,uBAAuB,CACrF,CAAA;QACD,gBAAgB,GAAG,OAAO,CAAA;IAC5B,CAAC;IAED,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAA;AAC9C,CAAC"}
|
package/package.json
CHANGED
package/src/bin.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import '.'
|
|
2
|
+
import './index.js'
|
|
@@ -123,6 +123,45 @@ function queryParam(
|
|
|
123
123
|
return param
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
function scalarUpdateOperations(
|
|
127
|
+
baseSchema: SchemaObject | RefObject,
|
|
128
|
+
fieldType: string,
|
|
129
|
+
fieldKind: string,
|
|
130
|
+
): SchemaObject {
|
|
131
|
+
const ops: Record<string, SchemaObject | RefObject> = { set: baseSchema }
|
|
132
|
+
|
|
133
|
+
if (fieldKind === 'scalar' && NUMERIC_SCALAR_TYPES.has(fieldType)) {
|
|
134
|
+
ops.increment = baseSchema
|
|
135
|
+
ops.decrement = baseSchema
|
|
136
|
+
ops.multiply = baseSchema
|
|
137
|
+
ops.divide = baseSchema
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
oneOf: [
|
|
142
|
+
baseSchema,
|
|
143
|
+
{ type: 'object', properties: ops },
|
|
144
|
+
],
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function listScalarUpdateOperations(
|
|
149
|
+
itemSchema: SchemaObject | RefObject,
|
|
150
|
+
): SchemaObject {
|
|
151
|
+
return {
|
|
152
|
+
type: 'object',
|
|
153
|
+
properties: {
|
|
154
|
+
set: { type: 'array', items: itemSchema },
|
|
155
|
+
push: {
|
|
156
|
+
oneOf: [
|
|
157
|
+
itemSchema as SchemaObject,
|
|
158
|
+
{ type: 'array', items: itemSchema },
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
126
165
|
export function buildModelOpenApi(
|
|
127
166
|
modelName: string,
|
|
128
167
|
modelFields: ModelField[],
|
|
@@ -220,7 +259,7 @@ function generateOperationSchemas(
|
|
|
220
259
|
.flatMap((f) => f.relationFromFields!),
|
|
221
260
|
)
|
|
222
261
|
|
|
223
|
-
const
|
|
262
|
+
const requiredCreateScalars = fields
|
|
224
263
|
.filter(
|
|
225
264
|
(f) =>
|
|
226
265
|
(f.kind === 'scalar' || f.kind === 'enum') &&
|
|
@@ -231,36 +270,46 @@ function generateOperationSchemas(
|
|
|
231
270
|
)
|
|
232
271
|
.map((f) => f.name)
|
|
233
272
|
|
|
273
|
+
const requiredCreateManyScalars = fields
|
|
274
|
+
.filter(
|
|
275
|
+
(f) =>
|
|
276
|
+
(f.kind === 'scalar' || f.kind === 'enum') &&
|
|
277
|
+
f.isRequired &&
|
|
278
|
+
!f.hasDefaultValue &&
|
|
279
|
+
!f.isUpdatedAt,
|
|
280
|
+
)
|
|
281
|
+
.map((f) => f.name)
|
|
282
|
+
|
|
234
283
|
const createInputSchema: SchemaObject = {
|
|
235
284
|
type: 'object',
|
|
236
|
-
properties: fieldsToWriteProperties(fields),
|
|
285
|
+
properties: fieldsToWriteProperties(fields, 'create'),
|
|
237
286
|
}
|
|
238
|
-
if (
|
|
239
|
-
createInputSchema.required = [...
|
|
287
|
+
if (requiredCreateScalars.length > 0) {
|
|
288
|
+
createInputSchema.required = [...requiredCreateScalars]
|
|
240
289
|
}
|
|
241
290
|
|
|
242
291
|
spec.components.schemas[`${modelName}CreateInput`] = createInputSchema
|
|
243
292
|
|
|
244
293
|
spec.components.schemas[`${modelName}UpdateInput`] = {
|
|
245
294
|
type: 'object',
|
|
246
|
-
properties: fieldsToWriteProperties(fields),
|
|
295
|
+
properties: fieldsToWriteProperties(fields, 'update'),
|
|
247
296
|
}
|
|
248
297
|
|
|
249
298
|
const createManyInputSchema: SchemaObject = {
|
|
250
299
|
type: 'object',
|
|
251
|
-
properties: fieldsToBulkWriteProperties(fields),
|
|
300
|
+
properties: fieldsToBulkWriteProperties(fields, 'create'),
|
|
252
301
|
description:
|
|
253
302
|
'Scalar-only input for bulk create. Nested relation writes are not supported in createMany operations.',
|
|
254
303
|
}
|
|
255
|
-
if (
|
|
256
|
-
createManyInputSchema.required = [...
|
|
304
|
+
if (requiredCreateManyScalars.length > 0) {
|
|
305
|
+
createManyInputSchema.required = [...requiredCreateManyScalars]
|
|
257
306
|
}
|
|
258
307
|
|
|
259
308
|
spec.components.schemas[`${modelName}CreateManyInput`] = createManyInputSchema
|
|
260
309
|
|
|
261
310
|
spec.components.schemas[`${modelName}UpdateManyMutationInput`] = {
|
|
262
311
|
type: 'object',
|
|
263
|
-
properties: fieldsToBulkWriteProperties(fields),
|
|
312
|
+
properties: fieldsToBulkWriteProperties(fields, 'update'),
|
|
264
313
|
description:
|
|
265
314
|
'Scalar-only input for bulk update. Nested relation writes are not supported in updateMany operations.',
|
|
266
315
|
}
|
|
@@ -1092,21 +1141,23 @@ function fieldsToProperties(
|
|
|
1092
1141
|
|
|
1093
1142
|
function fieldsToWriteProperties(
|
|
1094
1143
|
fields: ModelField[],
|
|
1144
|
+
mode: 'create' | 'update',
|
|
1095
1145
|
): Record<string, SchemaObject | RefObject> {
|
|
1096
1146
|
const props: Record<string, SchemaObject | RefObject> = {}
|
|
1097
1147
|
for (const field of fields) {
|
|
1098
|
-
props[field.name] = mapFieldToWriteSchema(field)
|
|
1148
|
+
props[field.name] = mapFieldToWriteSchema(field, mode)
|
|
1099
1149
|
}
|
|
1100
1150
|
return props
|
|
1101
1151
|
}
|
|
1102
1152
|
|
|
1103
1153
|
function fieldsToBulkWriteProperties(
|
|
1104
1154
|
fields: ModelField[],
|
|
1155
|
+
mode: 'create' | 'update',
|
|
1105
1156
|
): Record<string, SchemaObject | RefObject> {
|
|
1106
1157
|
const props: Record<string, SchemaObject | RefObject> = {}
|
|
1107
1158
|
for (const field of fields) {
|
|
1108
1159
|
if (field.kind === 'object') continue
|
|
1109
|
-
props[field.name] = mapFieldToWriteSchema(field)
|
|
1160
|
+
props[field.name] = mapFieldToWriteSchema(field, mode)
|
|
1110
1161
|
}
|
|
1111
1162
|
return props
|
|
1112
1163
|
}
|
|
@@ -1171,7 +1222,10 @@ function mapFieldToSchema(field: ModelField): SchemaObject | RefObject {
|
|
|
1171
1222
|
return schema
|
|
1172
1223
|
}
|
|
1173
1224
|
|
|
1174
|
-
function mapFieldToWriteSchema(
|
|
1225
|
+
function mapFieldToWriteSchema(
|
|
1226
|
+
field: ModelField,
|
|
1227
|
+
mode: 'create' | 'update',
|
|
1228
|
+
): SchemaObject | RefObject {
|
|
1175
1229
|
if (field.kind === 'object') {
|
|
1176
1230
|
if (field.isList) {
|
|
1177
1231
|
return {
|
|
@@ -1389,21 +1443,31 @@ function mapFieldToWriteSchema(field: ModelField): SchemaObject | RefObject {
|
|
|
1389
1443
|
}
|
|
1390
1444
|
}
|
|
1391
1445
|
|
|
1392
|
-
let
|
|
1446
|
+
let baseSchema: SchemaObject | RefObject
|
|
1393
1447
|
|
|
1394
1448
|
switch (field.kind) {
|
|
1395
1449
|
case 'scalar':
|
|
1396
|
-
|
|
1450
|
+
baseSchema = mapScalarType(field.type)
|
|
1397
1451
|
break
|
|
1398
1452
|
case 'enum':
|
|
1399
|
-
|
|
1453
|
+
baseSchema = { $ref: `#/components/schemas/${field.type}` }
|
|
1400
1454
|
break
|
|
1401
1455
|
default:
|
|
1402
|
-
|
|
1456
|
+
baseSchema = { type: 'string' }
|
|
1403
1457
|
}
|
|
1404
1458
|
|
|
1459
|
+
let schema: SchemaObject | RefObject
|
|
1460
|
+
|
|
1405
1461
|
if (field.isList) {
|
|
1406
|
-
|
|
1462
|
+
if (mode === 'update') {
|
|
1463
|
+
schema = listScalarUpdateOperations(baseSchema)
|
|
1464
|
+
} else {
|
|
1465
|
+
schema = { type: 'array', items: baseSchema }
|
|
1466
|
+
}
|
|
1467
|
+
} else if (mode === 'update') {
|
|
1468
|
+
schema = scalarUpdateOperations(baseSchema, field.type, field.kind)
|
|
1469
|
+
} else {
|
|
1470
|
+
schema = baseSchema
|
|
1407
1471
|
}
|
|
1408
1472
|
|
|
1409
1473
|
if (!field.isRequired && !field.isList) {
|
|
@@ -1417,7 +1481,7 @@ function mapFieldToWriteSchema(field: ModelField): SchemaObject | RefObject {
|
|
|
1417
1481
|
description: field.documentation,
|
|
1418
1482
|
}
|
|
1419
1483
|
} else if (!('$ref' in schema)) {
|
|
1420
|
-
schema.description = field.documentation
|
|
1484
|
+
(schema as SchemaObject).description = field.documentation
|
|
1421
1485
|
}
|
|
1422
1486
|
}
|
|
1423
1487
|
|
|
@@ -1573,4 +1637,4 @@ function toYaml(obj: any, indent = 0): string {
|
|
|
1573
1637
|
}
|
|
1574
1638
|
|
|
1575
1639
|
return yaml
|
|
1576
|
-
}
|
|
1640
|
+
}
|
|
@@ -57,10 +57,10 @@ import {
|
|
|
57
57
|
${modelName}Aggregate,
|
|
58
58
|
${modelName}Count,
|
|
59
59
|
${modelName}GroupBy
|
|
60
|
-
} from './${modelName}Handlers'
|
|
61
|
-
import type { RouteConfig } from '../routeConfig'
|
|
62
|
-
import { parseQueryParams } from '../parseQueryParams'
|
|
63
|
-
import { buildModelOpenApi } from '../buildModelOpenApi'
|
|
60
|
+
} from './${modelName}Handlers.js'
|
|
61
|
+
import type { RouteConfig } from '../routeConfig.js'
|
|
62
|
+
import { parseQueryParams } from '../parseQueryParams.js'
|
|
63
|
+
import { buildModelOpenApi } from '../buildModelOpenApi.js'
|
|
64
64
|
|
|
65
65
|
const _env = typeof process !== 'undefined' && process.env ? process.env : {} as Record<string, string | undefined>
|
|
66
66
|
|
|
@@ -140,7 +140,7 @@ export function ${routerFunctionName}(config: RouteConfig = {}) {
|
|
|
140
140
|
if (qbEnabled) {
|
|
141
141
|
const qbConfig = getQueryBuilderConfig(config)
|
|
142
142
|
if (qbConfig) {
|
|
143
|
-
import('../queryBuilder').then(mod => mod.startQueryBuilder(qbConfig)).catch(() => {})
|
|
143
|
+
import('../queryBuilder.js').then(mod => mod.startQueryBuilder(qbConfig)).catch((err) => { if (_env.NODE_ENV !== 'production') console.warn('[query-builder]', err) })
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
|
|
@@ -3,6 +3,10 @@ export function generateUnifiedDocs(models: string[]): string {
|
|
|
3
3
|
.map((model) => `import { ${model}Docs } from './${model}/${model}Docs'`)
|
|
4
4
|
.join('\n')
|
|
5
5
|
|
|
6
|
+
const handlersEntries = models
|
|
7
|
+
.map((model) => ` ${model}: ${model}Docs`)
|
|
8
|
+
.join(',\n')
|
|
9
|
+
|
|
6
10
|
return `${imports}
|
|
7
11
|
import { Request, Response } from 'express'
|
|
8
12
|
import type { RouteConfig } from './routeConfig'
|
|
@@ -10,7 +14,7 @@ import type { RouteConfig } from './routeConfig'
|
|
|
10
14
|
const _env = typeof process !== 'undefined' && process.env ? process.env : {} as Record<string, string | undefined>
|
|
11
15
|
|
|
12
16
|
const docsHandlers: Record<string, (config: any) => (req: Request, res: Response) => any> = {
|
|
13
|
-
${
|
|
17
|
+
${handlersEntries}
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
type DocsUI = 'docs' | 'scalar' | 'json' | 'yaml' | 'playground'
|
|
@@ -77,66 +81,72 @@ export function generateCombinedDocs(config: CombinedDocsConfig) {
|
|
|
77
81
|
const basePath = removeTrailingSlash(config.basePath || '/docs')
|
|
78
82
|
const generatedAt = new Date().toISOString()
|
|
79
83
|
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
</
|
|
139
|
-
</
|
|
84
|
+
const modelRows = registeredModels.map((m) => {
|
|
85
|
+
const lower = m.toLowerCase()
|
|
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
|
+
: ''
|
|
114
|
+
|
|
115
|
+
const html =
|
|
116
|
+
'<!DOCTYPE html>' +
|
|
117
|
+
'<html lang="en">' +
|
|
118
|
+
'<head>' +
|
|
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>'
|
|
140
150
|
|
|
141
151
|
res.type('html').send(html)
|
|
142
152
|
}
|
|
@@ -159,9 +169,9 @@ export function registerModelDocs(
|
|
|
159
169
|
registeredModels.forEach((model) => {
|
|
160
170
|
const handler = docsHandlers[model]
|
|
161
171
|
const cfg = configs[model] || {}
|
|
162
|
-
const
|
|
163
|
-
console.log(
|
|
164
|
-
app.get(
|
|
172
|
+
const docPath = normalizedBase + '/' + model.toLowerCase()
|
|
173
|
+
console.log(' Registered docs: ' + docPath)
|
|
174
|
+
app.get(docPath, handler(cfg))
|
|
165
175
|
})
|
|
166
176
|
}
|
|
167
177
|
`
|
|
@@ -152,6 +152,7 @@ function handleError(error: unknown, next: NextFunction): void {
|
|
|
152
152
|
return
|
|
153
153
|
}
|
|
154
154
|
if (typeof code === 'string' && code.startsWith('P')) {
|
|
155
|
+
console.warn('[prisma-generator-express] Unmapped Prisma error code:', code, (error as any).message || '')
|
|
155
156
|
next(new HttpError(500, 'Database operation failed'))
|
|
156
157
|
return
|
|
157
158
|
}
|
|
@@ -422,6 +423,14 @@ async function countForPagination(
|
|
|
422
423
|
const countShape = shape ? buildCountShape(shape) : undefined
|
|
423
424
|
|
|
424
425
|
if (hasDistinct) {
|
|
426
|
+
if (shape) {
|
|
427
|
+
const countArgs: Record<string, any> = {}
|
|
428
|
+
if (query.where) countArgs.where = query.where
|
|
429
|
+
return countShape
|
|
430
|
+
? await delegate.guard(countShape, caller).count(countArgs)
|
|
431
|
+
: await delegate.count(countArgs)
|
|
432
|
+
}
|
|
433
|
+
|
|
425
434
|
const selectField = distinctFields[0]
|
|
426
435
|
const distinctArgs: Record<string, any> = {
|
|
427
436
|
where: query.where,
|
|
@@ -430,17 +439,13 @@ async function countForPagination(
|
|
|
430
439
|
take: DISTINCT_COUNT_LIMIT + 1,
|
|
431
440
|
}
|
|
432
441
|
|
|
433
|
-
const results =
|
|
434
|
-
? await delegate.guard(shape, caller).findMany(distinctArgs)
|
|
435
|
-
: await delegate.findMany(distinctArgs)
|
|
442
|
+
const results = await delegate.findMany(distinctArgs)
|
|
436
443
|
|
|
437
444
|
if (results.length > DISTINCT_COUNT_LIMIT) {
|
|
438
445
|
console.warn('[prisma-generator-express] Distinct count exceeds ' + DISTINCT_COUNT_LIMIT + ', falling back to approximate total')
|
|
439
446
|
const countArgs: Record<string, any> = {}
|
|
440
447
|
if (query.where) countArgs.where = query.where
|
|
441
|
-
return
|
|
442
|
-
? await delegate.guard(countShape, caller).count(countArgs)
|
|
443
|
-
: await delegate.count(countArgs)
|
|
448
|
+
return await delegate.count(countArgs)
|
|
444
449
|
}
|
|
445
450
|
|
|
446
451
|
return results.length
|
|
@@ -513,4 +518,4 @@ export async function ${modelName}FindManyPaginated(req: Request, res: Response,
|
|
|
513
518
|
}
|
|
514
519
|
`
|
|
515
520
|
)
|
|
516
|
-
}
|
|
521
|
+
}
|
|
@@ -87,9 +87,9 @@ export function generateScalarUIHandler(options: {
|
|
|
87
87
|
}))
|
|
88
88
|
|
|
89
89
|
return `import { Request, Response } from 'express'
|
|
90
|
-
import { buildModelOpenApi } from '../buildModelOpenApi'
|
|
91
|
-
import type { RouteConfig } from '../routeConfig'
|
|
92
|
-
import { OPERATION_DEFS, isOperationEnabled } from '../operationDefinitions'
|
|
90
|
+
import { buildModelOpenApi } from '../buildModelOpenApi.js'
|
|
91
|
+
import type { RouteConfig } from '../routeConfig.js'
|
|
92
|
+
import { OPERATION_DEFS, isOperationEnabled } from '../operationDefinitions.js'
|
|
93
93
|
|
|
94
94
|
const _env = typeof process !== 'undefined' && process.env ? process.env : {} as Record<string, string | undefined>
|
|
95
95
|
|
package/src/index.ts
CHANGED
|
@@ -3,18 +3,18 @@ import {
|
|
|
3
3
|
GeneratorOptions,
|
|
4
4
|
DMMF,
|
|
5
5
|
} from '@prisma/generator-helper'
|
|
6
|
-
import { generateUnifiedHandler } from './generators/generateUnifiedHandler'
|
|
7
|
-
import { generateRouterFunction } from './generators/generateRouter'
|
|
8
|
-
import { generateScalarUIHandler } from './generators/generateUnifiedScalarUI'
|
|
9
|
-
import { generateUnifiedDocs } from './generators/generateUnifiedDocs'
|
|
10
|
-
import { generateQueryBuilderHelper } from './generators/generateQueryBuilderHelper'
|
|
6
|
+
import { generateUnifiedHandler } from './generators/generateUnifiedHandler.js'
|
|
7
|
+
import { generateRouterFunction } from './generators/generateRouter.js'
|
|
8
|
+
import { generateScalarUIHandler } from './generators/generateUnifiedScalarUI.js'
|
|
9
|
+
import { generateUnifiedDocs } from './generators/generateUnifiedDocs.js'
|
|
10
|
+
import { generateQueryBuilderHelper } from './generators/generateQueryBuilderHelper.js'
|
|
11
11
|
import {
|
|
12
12
|
generateImportPrismaStatement,
|
|
13
13
|
getRelativeClientPath,
|
|
14
|
-
} from './generators/generateImportPrismaStatement'
|
|
15
|
-
import { writeFileSafely } from './utils/writeFileSafely'
|
|
16
|
-
import { copyFiles } from './utils/copyFiles'
|
|
17
|
-
import { GENERATOR_NAME } from './constants'
|
|
14
|
+
} from './generators/generateImportPrismaStatement.js'
|
|
15
|
+
import { writeFileSafely } from './utils/writeFileSafely.js'
|
|
16
|
+
import { copyFiles } from './utils/copyFiles.js'
|
|
17
|
+
import { GENERATOR_NAME } from './constants.js'
|
|
18
18
|
|
|
19
19
|
generatorHandler({
|
|
20
20
|
onManifest() {
|
|
@@ -33,14 +33,6 @@ export async function writeFileSafely({
|
|
|
33
33
|
let filePath: string
|
|
34
34
|
|
|
35
35
|
switch (operation) {
|
|
36
|
-
case 'cacheConfig':
|
|
37
|
-
filePath = path.join(outputPath, 'cacheConfig.ts')
|
|
38
|
-
break
|
|
39
|
-
|
|
40
|
-
case 'types/inputs':
|
|
41
|
-
filePath = path.join(outputPath, 'types', 'inputs.ts')
|
|
42
|
-
break
|
|
43
|
-
|
|
44
36
|
case 'combinedDocs':
|
|
45
37
|
filePath = path.join(outputPath, 'combinedDocs.ts')
|
|
46
38
|
break
|
|
@@ -80,4 +72,4 @@ export async function writeFileSafely({
|
|
|
80
72
|
}
|
|
81
73
|
|
|
82
74
|
fs.writeFileSync(filePath, formattedContent)
|
|
83
|
-
}
|
|
75
|
+
}
|