@vibeorm/generator 1.1.5 → 1.1.6
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeorm/generator",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.6",
|
|
4
4
|
"description": "TypeScript client generator for VibeORM — produces typed delegates, inputs, and Zod schemas from a Prisma schema",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -37,6 +37,6 @@
|
|
|
37
37
|
"bun": ">=1.1.0"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@vibeorm/parser": "1.1.
|
|
40
|
+
"@vibeorm/parser": "1.1.5"
|
|
41
41
|
}
|
|
42
42
|
}
|
|
@@ -73,6 +73,9 @@ export type ViewResultWrapper<T> = T & {
|
|
|
73
73
|
toJSON(): Partial<T>;
|
|
74
74
|
$unsafe(): T;
|
|
75
75
|
};
|
|
76
|
+
|
|
77
|
+
/** Flatten intersection types for cleaner hover tooltips and type resolution */
|
|
78
|
+
type Compute<T> = T extends Function ? T : { [K in keyof T]: T[K] } & unknown;
|
|
76
79
|
`);
|
|
77
80
|
|
|
78
81
|
// ─── ViewDefinitionInput ──────────────────────────────────────────
|
|
@@ -109,6 +112,18 @@ export type ViewResultWrapper<T> = T & {
|
|
|
109
112
|
}
|
|
110
113
|
parts.push(``);
|
|
111
114
|
|
|
115
|
+
// ─── Per-model ViewIncludeResult ──────────────────────────────────
|
|
116
|
+
|
|
117
|
+
for (const model of schema.models) {
|
|
118
|
+
parts.push(generateViewIncludeResult({ model, schema }));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ─── Per-model GetViewResult ────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
for (const model of schema.models) {
|
|
124
|
+
parts.push(generateGetViewResult({ model, schema }));
|
|
125
|
+
}
|
|
126
|
+
|
|
112
127
|
// ─── Per-model ViewFindArgs ───────────────────────────────────────
|
|
113
128
|
|
|
114
129
|
for (const model of schema.models) {
|
|
@@ -128,6 +143,114 @@ export type ViewResultWrapper<T> = T & {
|
|
|
128
143
|
return parts.join("\n");
|
|
129
144
|
}
|
|
130
145
|
|
|
146
|
+
// ─── ViewIncludeResult (per model) ────────────────────────────────
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Generates a mapped type that computes the result types for included
|
|
150
|
+
* relations on a view query. For each relation field:
|
|
151
|
+
*
|
|
152
|
+
* - If the related model is in VD: result is ViewResultWrapper<Pick<Model, viewFields>>
|
|
153
|
+
* - If the related model is NOT in VD: result is the full model type
|
|
154
|
+
* - Array relations get [], nullable singles get | null
|
|
155
|
+
*/
|
|
156
|
+
function generateViewIncludeResult(params: { model: Model; schema: Schema }): string {
|
|
157
|
+
const { model } = params;
|
|
158
|
+
const n = model.name;
|
|
159
|
+
|
|
160
|
+
const relationFields = model.fields.filter(
|
|
161
|
+
(f): f is RelationField => f.kind === "relation"
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
if (relationFields.length === 0) {
|
|
165
|
+
// No relations — include result is always empty
|
|
166
|
+
return `
|
|
167
|
+
// ─── ${n} View Include Result ────────────────────────────────
|
|
168
|
+
|
|
169
|
+
type ${n}ViewIncludeResult<
|
|
170
|
+
I extends Record<string, unknown>,
|
|
171
|
+
VD extends ViewDefinitionInput,
|
|
172
|
+
> = {};
|
|
173
|
+
`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Build nested conditional branches: K extends "relName" ? ... : K extends ... : never
|
|
177
|
+
const branches = relationFields.map((f) => {
|
|
178
|
+
const relModelVar = toCamelCase({ str: f.relatedModel });
|
|
179
|
+
const relModel = f.relatedModel;
|
|
180
|
+
|
|
181
|
+
// Type when the related model IS in the view definition
|
|
182
|
+
const viewRestricted = `ViewResultWrapper<Pick<${relModel}, keyof RF & keyof ${relModel}>>`;
|
|
183
|
+
// Type when the related model is NOT in the view definition
|
|
184
|
+
const fullType = relModel;
|
|
185
|
+
|
|
186
|
+
let inViewType: string;
|
|
187
|
+
let notInViewType: string;
|
|
188
|
+
|
|
189
|
+
if (f.isList) {
|
|
190
|
+
inViewType = `${viewRestricted}[]`;
|
|
191
|
+
notInViewType = `${fullType}[]`;
|
|
192
|
+
} else if (!f.isRequired) {
|
|
193
|
+
inViewType = `${viewRestricted} | null`;
|
|
194
|
+
notInViewType = `${fullType} | null`;
|
|
195
|
+
} else {
|
|
196
|
+
inViewType = viewRestricted;
|
|
197
|
+
notInViewType = fullType;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return ` K extends "${f.name}"
|
|
201
|
+
? (VD extends { ${relModelVar}: infer RF extends Record<string, true> }
|
|
202
|
+
? ${inViewType}
|
|
203
|
+
: ${notInViewType})`;
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
return `
|
|
207
|
+
// ─── ${n} View Include Result ────────────────────────────────
|
|
208
|
+
|
|
209
|
+
type ${n}ViewIncludeResult<
|
|
210
|
+
I extends Record<string, unknown>,
|
|
211
|
+
VD extends ViewDefinitionInput,
|
|
212
|
+
> = {
|
|
213
|
+
[K in keyof I as I[K] extends false | undefined | null ? never : K]:
|
|
214
|
+
${branches.join("\n : ")}
|
|
215
|
+
: never;
|
|
216
|
+
};
|
|
217
|
+
`;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ─── GetViewResult (per model) ────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Generates a top-level conditional type that resolves the complete result
|
|
224
|
+
* type for a view query, including both scalar fields and included relations.
|
|
225
|
+
*
|
|
226
|
+
* The conditional is at the outermost position (matching how GetFindResult
|
|
227
|
+
* works in the regular client) so TypeScript evaluates it eagerly at call
|
|
228
|
+
* sites instead of deferring it inside ViewResultWrapper's type parameter.
|
|
229
|
+
*/
|
|
230
|
+
function generateGetViewResult(params: { model: Model; schema: Schema }): string {
|
|
231
|
+
const { model } = params;
|
|
232
|
+
const n = model.name;
|
|
233
|
+
const hasRelations = model.fields.some((f) => f.kind === "relation");
|
|
234
|
+
|
|
235
|
+
if (!hasRelations) return "";
|
|
236
|
+
|
|
237
|
+
return `
|
|
238
|
+
// ─── ${n} Get View Result ───────────────────────────────────
|
|
239
|
+
|
|
240
|
+
type ${n}GetViewResult<
|
|
241
|
+
VF extends Record<string, true>,
|
|
242
|
+
T,
|
|
243
|
+
VD extends ViewDefinitionInput,
|
|
244
|
+
> =
|
|
245
|
+
T extends { include: infer I extends Record<string, unknown> }
|
|
246
|
+
? ViewResultWrapper<Compute<
|
|
247
|
+
ViewSelectResult<${n}, VF, T>
|
|
248
|
+
& ${n}ViewIncludeResult<I, VD>
|
|
249
|
+
>>
|
|
250
|
+
: ViewResultWrapper<ViewSelectResult<${n}, VF, T>>;
|
|
251
|
+
`;
|
|
252
|
+
}
|
|
253
|
+
|
|
131
254
|
// ─── ViewFindArgs (per model) ─────────────────────────────────────
|
|
132
255
|
|
|
133
256
|
function generateViewFindArgs(params: { model: Model; schema: Schema }): string {
|
|
@@ -195,6 +318,20 @@ function generateViewDelegate(params: { model: Model; schema: Schema }): string
|
|
|
195
318
|
const { model } = params;
|
|
196
319
|
const n = model.name;
|
|
197
320
|
|
|
321
|
+
const hasRelations = model.fields.some((f) => f.kind === "relation");
|
|
322
|
+
|
|
323
|
+
// Helper: builds the full result type including the include intersection.
|
|
324
|
+
// Models with relations use a dedicated GetViewResult type that places the
|
|
325
|
+
// conditional at the outermost position (matching GetFindResult in the regular
|
|
326
|
+
// client) so TypeScript evaluates it eagerly instead of deferring it inside
|
|
327
|
+
// ViewResultWrapper's type parameter.
|
|
328
|
+
const resultType = (argsType: string) => {
|
|
329
|
+
if (!hasRelations) {
|
|
330
|
+
return `ViewResultWrapper<ViewSelectResult<${n}, VF, ${argsType}>>`;
|
|
331
|
+
}
|
|
332
|
+
return `${n}GetViewResult<VF, ${argsType}, VD>`;
|
|
333
|
+
};
|
|
334
|
+
|
|
198
335
|
return `
|
|
199
336
|
// ─── ${n} View Delegate ─────────────────────────────────────
|
|
200
337
|
|
|
@@ -207,45 +344,35 @@ export type ${n}ViewDelegate<
|
|
|
207
344
|
*/
|
|
208
345
|
findMany<T extends ${n}ViewFindManyArgs<VF, VD>>(
|
|
209
346
|
args?: Exact<T, ${n}ViewFindManyArgs<VF, VD>>
|
|
210
|
-
): Promise
|
|
211
|
-
ViewSelectResult<${n}, VF, T>
|
|
212
|
-
>[]>;
|
|
347
|
+
): Promise<${resultType("T")}[]>;
|
|
213
348
|
|
|
214
349
|
/**
|
|
215
350
|
* Find the first ${n} matching the filter, or null.
|
|
216
351
|
*/
|
|
217
352
|
findFirst<T extends ${n}ViewFindFirstArgs<VF, VD>>(
|
|
218
353
|
args?: Exact<T, ${n}ViewFindFirstArgs<VF, VD>>
|
|
219
|
-
): Promise
|
|
220
|
-
ViewSelectResult<${n}, VF, T>
|
|
221
|
-
> | null>;
|
|
354
|
+
): Promise<${resultType("T")} | null>;
|
|
222
355
|
|
|
223
356
|
/**
|
|
224
357
|
* Find a unique ${n} by its primary key or unique field, or null.
|
|
225
358
|
*/
|
|
226
359
|
findUnique<T extends ${n}ViewFindUniqueArgs<VF, VD>>(
|
|
227
360
|
args: Exact<T, ${n}ViewFindUniqueArgs<VF, VD>>
|
|
228
|
-
): Promise
|
|
229
|
-
ViewSelectResult<${n}, VF, T>
|
|
230
|
-
> | null>;
|
|
361
|
+
): Promise<${resultType("T")} | null>;
|
|
231
362
|
|
|
232
363
|
/**
|
|
233
364
|
* Find a unique ${n} or throw if not found.
|
|
234
365
|
*/
|
|
235
366
|
findUniqueOrThrow<T extends ${n}ViewFindUniqueArgs<VF, VD>>(
|
|
236
367
|
args: Exact<T, ${n}ViewFindUniqueArgs<VF, VD>>
|
|
237
|
-
): Promise
|
|
238
|
-
ViewSelectResult<${n}, VF, T>
|
|
239
|
-
>>;
|
|
368
|
+
): Promise<${resultType("T")}>;
|
|
240
369
|
|
|
241
370
|
/**
|
|
242
371
|
* Find the first ${n} matching the filter, or throw.
|
|
243
372
|
*/
|
|
244
373
|
findFirstOrThrow<T extends ${n}ViewFindFirstArgs<VF, VD>>(
|
|
245
374
|
args?: Exact<T, ${n}ViewFindFirstArgs<VF, VD>>
|
|
246
|
-
): Promise
|
|
247
|
-
ViewSelectResult<${n}, VF, T>
|
|
248
|
-
>>;
|
|
375
|
+
): Promise<${resultType("T")}>;
|
|
249
376
|
|
|
250
377
|
/**
|
|
251
378
|
* Count matching ${n} records.
|