prisma-generator-express 1.42.0 → 1.44.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/generators/generateFastifyHandler.d.ts +2 -0
- package/dist/generators/generateFastifyHandler.js +21 -6
- package/dist/generators/generateFastifyHandler.js.map +1 -1
- package/dist/generators/generateHonoHandler.d.ts +2 -0
- package/dist/generators/generateHonoHandler.js +8 -6
- package/dist/generators/generateHonoHandler.js.map +1 -1
- package/dist/generators/generateOperationCore.d.ts +0 -1
- package/dist/generators/generateOperationCore.js +34 -546
- package/dist/generators/generateOperationCore.js.map +1 -1
- package/dist/generators/generateRelationMeta.d.ts +13 -0
- package/dist/generators/generateRelationMeta.js +110 -0
- package/dist/generators/generateRelationMeta.js.map +1 -0
- package/dist/generators/generateRouteConfigType.js +13 -5
- package/dist/generators/generateRouteConfigType.js.map +1 -1
- package/dist/generators/generateRouter.js +83 -19
- package/dist/generators/generateRouter.js.map +1 -1
- package/dist/generators/generateRouterFastify.js +127 -384
- package/dist/generators/generateRouterFastify.js.map +1 -1
- package/dist/generators/generateRouterHono.js +48 -36
- package/dist/generators/generateRouterHono.js.map +1 -1
- package/dist/generators/generateUnifiedHandler.d.ts +2 -0
- package/dist/generators/generateUnifiedHandler.js +28 -10
- package/dist/generators/generateUnifiedHandler.js.map +1 -1
- package/dist/generators/generateUnifiedScalarUI.d.ts +2 -0
- package/dist/generators/generateUnifiedScalarUI.js +19 -16
- package/dist/generators/generateUnifiedScalarUI.js.map +1 -1
- package/dist/index.js +21 -5
- package/dist/index.js.map +1 -1
- package/dist/utils/copyFiles.js +12 -0
- package/dist/utils/copyFiles.js.map +1 -1
- package/dist/utils/writeFileSafely.js +2 -2
- package/dist/utils/writeFileSafely.js.map +1 -1
- package/package.json +1 -1
- package/src/copy/autoIncludePlanner.ts +354 -0
- package/src/copy/autoIncludeRuntime.ts +362 -0
- package/src/copy/operationRuntime.ts +614 -0
- package/src/copy/routeConfig.express.ts +5 -3
- package/src/copy/routeConfig.fastify.ts +2 -2
- package/src/copy/routeConfig.hono.ts +3 -3
- package/src/copy/routeConfig.ts +20 -9
- package/src/generators/generateFastifyHandler.ts +23 -6
- package/src/generators/generateHonoHandler.ts +10 -6
- package/src/generators/generateOperationCore.ts +34 -546
- package/src/generators/generateRelationMeta.ts +160 -0
- package/src/generators/generateRouteConfigType.ts +13 -6
- package/src/generators/generateRouter.ts +83 -19
- package/src/generators/generateRouterFastify.ts +127 -384
- package/src/generators/generateRouterHono.ts +48 -36
- package/src/generators/generateUnifiedHandler.ts +30 -10
- package/src/generators/generateUnifiedScalarUI.ts +21 -16
- package/src/index.ts +31 -13
- package/src/utils/copyFiles.ts +13 -0
- package/src/utils/writeFileSafely.ts +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copyFiles.js","sourceRoot":"","sources":["../../src/utils/copyFiles.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"copyFiles.js","sourceRoot":"","sources":["../../src/utils/copyFiles.ts"],"names":[],"mappings":"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"}
|
|
@@ -59,8 +59,8 @@ async function writeFileSafely({ content, options, model, operation, }) {
|
|
|
59
59
|
case 'queryBuilder':
|
|
60
60
|
filePath = path.join(outputPath, 'queryBuilder.ts');
|
|
61
61
|
break;
|
|
62
|
-
case '
|
|
63
|
-
filePath = path.join(outputPath, '
|
|
62
|
+
case 'relationModelsIndex':
|
|
63
|
+
filePath = path.join(outputPath, 'relationModels.ts');
|
|
64
64
|
break;
|
|
65
65
|
default:
|
|
66
66
|
if (!model)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writeFileSafely.js","sourceRoot":"","sources":["../../src/utils/writeFileSafely.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqBA,0CAyCC;AA5DD,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;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAA;IAE3D,IAAI,QAAgB,CAAA;IACpB,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,cAAc;YACjB,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAA;YACnD,MAAK;QACP,KAAK,cAAc;YACjB,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAA;YACnD,MAAK;QACP,KAAK,
|
|
1
|
+
{"version":3,"file":"writeFileSafely.js","sourceRoot":"","sources":["../../src/utils/writeFileSafely.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqBA,0CAyCC;AA5DD,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;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAA;IAE3D,IAAI,QAAgB,CAAA;IACpB,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,cAAc;YACjB,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAA;YACnD,MAAK;QACP,KAAK,cAAc;YACjB,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAA;YACnD,MAAK;QACP,KAAK,qBAAqB;YACxB,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,mBAAmB,CAAC,CAAA;YACrD,MAAK;QACP;YACE,IAAI,CAAC,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,GAAG,SAAS,CAAC,CAAA;YACzE,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,IAAI,GAAG,SAAS,KAAK,CAAC,CAAA;IAChF,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IACtC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAEvE,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,MAAM,CAAC;QACP,OAAO,CAAC,IAAI,CAAC,qCAAqC,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,uBAAuB,CAAC,CAAA;QACvG,gBAAgB,GAAG,OAAO,CAAA;IAC5B,CAAC;IAED,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAA;AAC9C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prisma-generator-express",
|
|
3
3
|
"description": "Prisma generator for Express, Fastify, and Hono CRUD APIs with OpenAPI documentation",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.44.0",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"license": "MIT",
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
export type ModelRelationDirection = 'parentOwnsFk' | 'childOwnsFk' | 'implicitM2M'
|
|
2
|
+
|
|
3
|
+
export type ModelRelationField = {
|
|
4
|
+
name: string
|
|
5
|
+
type: string
|
|
6
|
+
isList: boolean
|
|
7
|
+
isRequired: boolean
|
|
8
|
+
direction: ModelRelationDirection
|
|
9
|
+
parentLinkFields: string[]
|
|
10
|
+
childLinkFields: string[]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type ModelRelationMap = {
|
|
14
|
+
name: string
|
|
15
|
+
delegateKey: string
|
|
16
|
+
scalarFields: string[]
|
|
17
|
+
idFields: string[]
|
|
18
|
+
relations: Record<string, ModelRelationField>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type AutoIncludeStage = {
|
|
22
|
+
relationPath: string
|
|
23
|
+
parentPath: string
|
|
24
|
+
relationName: string
|
|
25
|
+
relationField: ModelRelationField
|
|
26
|
+
stageArgs: Record<string, unknown>
|
|
27
|
+
depth: number
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type AutoIncludePlan = {
|
|
31
|
+
rootArgs: Record<string, unknown>
|
|
32
|
+
stages: AutoIncludeStage[]
|
|
33
|
+
internalFieldPaths: string[]
|
|
34
|
+
unsupportedReason?: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type AutoIncludePlannerInput = {
|
|
38
|
+
rootModelName: string
|
|
39
|
+
models: Record<string, ModelRelationMap>
|
|
40
|
+
args: Record<string, unknown>
|
|
41
|
+
maxDepth?: number
|
|
42
|
+
maxStages?: number
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const DEFAULT_AUTO_INCLUDE_MAX_DEPTH = 3
|
|
46
|
+
export const DEFAULT_AUTO_INCLUDE_MAX_STAGES = 20
|
|
47
|
+
|
|
48
|
+
const ALLOWED_TO_ONE_ARGS = new Set(['select', 'include', 'omit'])
|
|
49
|
+
const ALLOWED_TO_MANY_ARGS = new Set([
|
|
50
|
+
'select',
|
|
51
|
+
'include',
|
|
52
|
+
'omit',
|
|
53
|
+
'where',
|
|
54
|
+
'orderBy',
|
|
55
|
+
'take',
|
|
56
|
+
'skip',
|
|
57
|
+
'cursor',
|
|
58
|
+
'distinct',
|
|
59
|
+
])
|
|
60
|
+
|
|
61
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
62
|
+
if (value === null || typeof value !== 'object') return false
|
|
63
|
+
if (Array.isArray(value)) return false
|
|
64
|
+
return true
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isPubliclySelected(projection: Record<string, unknown>, field: string): boolean {
|
|
68
|
+
return projection[field] === true
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function hasCountKey(value: unknown): boolean {
|
|
72
|
+
if (!isPlainObject(value)) return false
|
|
73
|
+
return '_count' in value
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function hasWhereLikeRelationReference(
|
|
77
|
+
args: Record<string, unknown>,
|
|
78
|
+
model: ModelRelationMap,
|
|
79
|
+
): boolean {
|
|
80
|
+
const checkObject = (obj: unknown): boolean => {
|
|
81
|
+
if (!isPlainObject(obj)) return false
|
|
82
|
+
for (const key of Object.keys(obj)) {
|
|
83
|
+
if (model.relations[key]) return true
|
|
84
|
+
if (key === 'AND' || key === 'OR' || key === 'NOT') {
|
|
85
|
+
const sub = obj[key]
|
|
86
|
+
if (Array.isArray(sub)) {
|
|
87
|
+
for (const item of sub) if (checkObject(item)) return true
|
|
88
|
+
} else if (checkObject(sub)) {
|
|
89
|
+
return true
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return false
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (checkObject(args.where)) return true
|
|
97
|
+
|
|
98
|
+
if (args.orderBy) {
|
|
99
|
+
const obs = Array.isArray(args.orderBy) ? args.orderBy : [args.orderBy]
|
|
100
|
+
for (const ob of obs) {
|
|
101
|
+
if (!isPlainObject(ob)) continue
|
|
102
|
+
for (const key of Object.keys(ob)) {
|
|
103
|
+
if (model.relations[key]) return true
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (isPlainObject(args.cursor)) {
|
|
109
|
+
for (const key of Object.keys(args.cursor)) {
|
|
110
|
+
if (model.relations[key]) return true
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return false
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
type WalkContext = {
|
|
118
|
+
models: Record<string, ModelRelationMap>
|
|
119
|
+
stages: AutoIncludeStage[]
|
|
120
|
+
internalFieldPaths: string[]
|
|
121
|
+
maxDepth: number
|
|
122
|
+
maxStages: number
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
type WalkResult = {
|
|
126
|
+
unsupportedReason?: string
|
|
127
|
+
projectionAfterStrip?: Record<string, unknown>
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function walk(
|
|
131
|
+
ctx: WalkContext,
|
|
132
|
+
modelName: string,
|
|
133
|
+
parentPath: string,
|
|
134
|
+
parentArgs: Record<string, unknown>,
|
|
135
|
+
depth: number,
|
|
136
|
+
): WalkResult {
|
|
137
|
+
if (depth >= ctx.maxDepth) {
|
|
138
|
+
return { unsupportedReason: 'nested depth reached maxDepth=' + ctx.maxDepth }
|
|
139
|
+
}
|
|
140
|
+
if (ctx.stages.length >= ctx.maxStages) {
|
|
141
|
+
return { unsupportedReason: 'stages reached maxStages=' + ctx.maxStages }
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const model = ctx.models[modelName]
|
|
145
|
+
if (!model) {
|
|
146
|
+
return { unsupportedReason: 'model ' + modelName + ' not in relation metadata' }
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const select = parentArgs.select
|
|
150
|
+
const include = parentArgs.include
|
|
151
|
+
const omit = parentArgs.omit
|
|
152
|
+
|
|
153
|
+
if (isPlainObject(select) && isPlainObject(include)) {
|
|
154
|
+
return { unsupportedReason: 'select+include at same level' }
|
|
155
|
+
}
|
|
156
|
+
if (isPlainObject(select) && isPlainObject(omit)) {
|
|
157
|
+
return { unsupportedReason: 'select+omit at same level' }
|
|
158
|
+
}
|
|
159
|
+
if (hasCountKey(select) || hasCountKey(include)) {
|
|
160
|
+
return { unsupportedReason: '_count not supported in MVP' }
|
|
161
|
+
}
|
|
162
|
+
if (hasWhereLikeRelationReference(parentArgs, model)) {
|
|
163
|
+
return { unsupportedReason: 'relation used in where/orderBy/cursor is not supported in MVP' }
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const projection = isPlainObject(select) ? select : (isPlainObject(include) ? include : null)
|
|
167
|
+
if (!projection) {
|
|
168
|
+
return {}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const isSelectMode = isPlainObject(select)
|
|
172
|
+
const localOmit = isPlainObject(omit) ? omit : null
|
|
173
|
+
const updatedProjection: Record<string, unknown> = {}
|
|
174
|
+
|
|
175
|
+
const relationBranches: Array<{ name: string; value: unknown }> = []
|
|
176
|
+
|
|
177
|
+
for (const [key, value] of Object.entries(projection)) {
|
|
178
|
+
if (model.relations[key]) {
|
|
179
|
+
if (value === false || value === null || value === undefined) {
|
|
180
|
+
if (isSelectMode) {
|
|
181
|
+
updatedProjection[key] = value
|
|
182
|
+
}
|
|
183
|
+
continue
|
|
184
|
+
}
|
|
185
|
+
relationBranches.push({ name: key, value })
|
|
186
|
+
} else {
|
|
187
|
+
updatedProjection[key] = value
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (relationBranches.length === 0) {
|
|
192
|
+
return { projectionAfterStrip: isSelectMode ? updatedProjection : undefined }
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
for (const branch of relationBranches) {
|
|
196
|
+
if (ctx.stages.length >= ctx.maxStages) {
|
|
197
|
+
return { unsupportedReason: 'stages reached maxStages=' + ctx.maxStages }
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const relation = model.relations[branch.name]
|
|
201
|
+
|
|
202
|
+
if (relation.direction === 'implicitM2M') {
|
|
203
|
+
return { unsupportedReason: 'implicit many-to-many not supported in MVP' }
|
|
204
|
+
}
|
|
205
|
+
if (relation.parentLinkFields.length === 0 || relation.childLinkFields.length === 0) {
|
|
206
|
+
return { unsupportedReason: 'ambiguous relation metadata for ' + relation.name }
|
|
207
|
+
}
|
|
208
|
+
if (relation.parentLinkFields.length !== relation.childLinkFields.length) {
|
|
209
|
+
return { unsupportedReason: 'mismatched link field counts for ' + relation.name }
|
|
210
|
+
}
|
|
211
|
+
if (!ctx.models[relation.type]) {
|
|
212
|
+
return {
|
|
213
|
+
unsupportedReason: 'target model ' + relation.type +
|
|
214
|
+
' not in relation metadata for ' + (parentPath ? parentPath + '.' : '') + branch.name,
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (branch.value !== true && !isPlainObject(branch.value)) {
|
|
219
|
+
return {
|
|
220
|
+
unsupportedReason: 'invalid relation projection value for ' + branch.name +
|
|
221
|
+
' (expected true or plain object)',
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
for (const linkField of relation.parentLinkFields) {
|
|
226
|
+
if (localOmit && localOmit[linkField] === true) {
|
|
227
|
+
const where = parentPath ? parentPath + '.' : 'root '
|
|
228
|
+
return { unsupportedReason: 'required parent link field omitted: ' + where + linkField }
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
for (const linkField of relation.parentLinkFields) {
|
|
233
|
+
if (isSelectMode && !isPubliclySelected(projection, linkField)) {
|
|
234
|
+
updatedProjection[linkField] = true
|
|
235
|
+
const fullPath = parentPath ? parentPath + '.' + linkField : linkField
|
|
236
|
+
ctx.internalFieldPaths.push(fullPath)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const relationArgs: Record<string, unknown> = branch.value === true ? {} : branch.value
|
|
241
|
+
|
|
242
|
+
const allowedArgs = relation.isList ? ALLOWED_TO_MANY_ARGS : ALLOWED_TO_ONE_ARGS
|
|
243
|
+
for (const key of Object.keys(relationArgs)) {
|
|
244
|
+
if (!allowedArgs.has(key)) {
|
|
245
|
+
return {
|
|
246
|
+
unsupportedReason: 'unsupported arg "' + key + '" for ' +
|
|
247
|
+
(relation.isList ? 'to-many' : 'to-one') + ' relation ' + relation.name,
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const targetModel = ctx.models[relation.type]
|
|
253
|
+
if (hasWhereLikeRelationReference(relationArgs, targetModel)) {
|
|
254
|
+
return {
|
|
255
|
+
unsupportedReason: 'nested relation used in where/orderBy/cursor for ' +
|
|
256
|
+
relation.name + ' is not supported in MVP',
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const relationPath = parentPath ? parentPath + '.' + branch.name : branch.name
|
|
261
|
+
|
|
262
|
+
const stageArgs: Record<string, unknown> = {}
|
|
263
|
+
for (const [k, v] of Object.entries(relationArgs)) {
|
|
264
|
+
if (k === 'select' || k === 'include' || k === 'omit') continue
|
|
265
|
+
stageArgs[k] = v
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const stageIndex = ctx.stages.length
|
|
269
|
+
|
|
270
|
+
ctx.stages.push({
|
|
271
|
+
relationPath,
|
|
272
|
+
parentPath,
|
|
273
|
+
relationName: branch.name,
|
|
274
|
+
relationField: relation,
|
|
275
|
+
stageArgs,
|
|
276
|
+
depth: depth + 1,
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
const hasNestedProjection =
|
|
280
|
+
isPlainObject(relationArgs.select) ||
|
|
281
|
+
isPlainObject(relationArgs.include) ||
|
|
282
|
+
isPlainObject(relationArgs.omit)
|
|
283
|
+
|
|
284
|
+
if (hasNestedProjection) {
|
|
285
|
+
const stagesBeforeRecursion = ctx.stages.length
|
|
286
|
+
|
|
287
|
+
const nested = walk(ctx, relation.type, relationPath, relationArgs, depth + 1)
|
|
288
|
+
if (nested.unsupportedReason) return nested
|
|
289
|
+
|
|
290
|
+
if (relation.isList && ctx.stages.length > stagesBeforeRecursion) {
|
|
291
|
+
return { unsupportedReason: 'nested relation through to-many parent not supported in MVP' }
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (nested.projectionAfterStrip) {
|
|
295
|
+
ctx.stages[stageIndex].stageArgs.select = nested.projectionAfterStrip
|
|
296
|
+
}
|
|
297
|
+
if (isPlainObject(relationArgs.omit)) {
|
|
298
|
+
ctx.stages[stageIndex].stageArgs.omit = relationArgs.omit
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return { projectionAfterStrip: isSelectMode ? updatedProjection : undefined }
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function planAutoInclude(input: AutoIncludePlannerInput): AutoIncludePlan {
|
|
307
|
+
const maxDepth = input.maxDepth ?? DEFAULT_AUTO_INCLUDE_MAX_DEPTH
|
|
308
|
+
const maxStages = input.maxStages ?? DEFAULT_AUTO_INCLUDE_MAX_STAGES
|
|
309
|
+
|
|
310
|
+
const ctx: WalkContext = {
|
|
311
|
+
models: input.models,
|
|
312
|
+
stages: [],
|
|
313
|
+
internalFieldPaths: [],
|
|
314
|
+
maxDepth,
|
|
315
|
+
maxStages,
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const result = walk(ctx, input.rootModelName, '', input.args, 0)
|
|
319
|
+
|
|
320
|
+
if (result.unsupportedReason) {
|
|
321
|
+
return {
|
|
322
|
+
rootArgs: input.args,
|
|
323
|
+
stages: [],
|
|
324
|
+
internalFieldPaths: [],
|
|
325
|
+
unsupportedReason: 'auto-progressive fallback: ' + result.unsupportedReason,
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const rootArgs: Record<string, unknown> = { ...input.args }
|
|
330
|
+
const rootModel = input.models[input.rootModelName]
|
|
331
|
+
|
|
332
|
+
if (result.projectionAfterStrip) {
|
|
333
|
+
rootArgs.select = result.projectionAfterStrip
|
|
334
|
+
delete rootArgs.include
|
|
335
|
+
} else if (rootModel && isPlainObject(input.args.include)) {
|
|
336
|
+
const stripped: Record<string, unknown> = {}
|
|
337
|
+
for (const [k, v] of Object.entries(input.args.include)) {
|
|
338
|
+
if (!rootModel.relations[k]) {
|
|
339
|
+
stripped[k] = v
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (Object.keys(stripped).length > 0) {
|
|
343
|
+
rootArgs.include = stripped
|
|
344
|
+
} else {
|
|
345
|
+
delete rootArgs.include
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
rootArgs,
|
|
351
|
+
stages: ctx.stages,
|
|
352
|
+
internalFieldPaths: ctx.internalFieldPaths,
|
|
353
|
+
}
|
|
354
|
+
}
|