@vibeorm/generator 1.3.1 → 2.0.0-alpha.2
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 +43 -39
- package/dist/emit-dts.d.ts +24 -0
- package/dist/emit-dts.d.ts.map +1 -0
- package/dist/emit-js.d.ts +18 -0
- package/dist/emit-js.d.ts.map +1 -0
- package/dist/extension-bake.d.ts +42 -0
- package/dist/extension-bake.d.ts.map +1 -0
- package/dist/generate.d.ts +28 -0
- package/dist/generate.d.ts.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1521 -0
- package/dist/index.js.map +16 -0
- package/dist/schema-view.d.ts +77 -0
- package/dist/schema-view.d.ts.map +1 -0
- package/dist/type-names.d.ts +30 -0
- package/dist/type-names.d.ts.map +1 -0
- package/dist/util.d.ts +39 -0
- package/dist/util.d.ts.map +1 -0
- package/package.json +29 -23
- package/src/generate.ts +0 -70
- package/src/generators/generate-args.ts +0 -322
- package/src/generators/generate-client.ts +0 -252
- package/src/generators/generate-delegates.ts +0 -590
- package/src/generators/generate-enums.ts +0 -28
- package/src/generators/generate-inputs.ts +0 -655
- package/src/generators/generate-models.ts +0 -130
- package/src/generators/generate-result.ts +0 -182
- package/src/generators/generate-schemas.ts +0 -474
- package/src/generators/generate-view-types.ts +0 -446
- package/src/index.ts +0 -2
- package/src/utils.ts +0 -51
|
@@ -1,322 +0,0 @@
|
|
|
1
|
-
import type { Model, Schema, RelationField, ScalarField } from "@vibeorm/parser";
|
|
2
|
-
import { fileHeader } from "../utils.ts";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Generates Select, Include, and Args types for each model,
|
|
6
|
-
* plus the FindManyArgs, FindFirstArgs, FindUniqueArgs, etc.
|
|
7
|
-
*/
|
|
8
|
-
export function generateArgs(params: { schema: Schema }): string {
|
|
9
|
-
const { schema } = params;
|
|
10
|
-
const parts: string[] = [fileHeader()];
|
|
11
|
-
|
|
12
|
-
// Imports
|
|
13
|
-
const modelImports = schema.models.map((m) => `${m.name}WhereInput, ${m.name}WhereUniqueInput, ${m.name}OrderByInput, ${m.name}CreateInput, ${m.name}UpdateInput`).join(", ");
|
|
14
|
-
parts.push(`import type { ${modelImports}, SortOrder, SortOrderWithNulls } from "./inputs.ts";`);
|
|
15
|
-
|
|
16
|
-
const payloadImports = schema.models.map((m) => `$${m.name}Payload`).join(", ");
|
|
17
|
-
parts.push(`import type { ${payloadImports}, OperationPayload } from "./models.ts";\n`);
|
|
18
|
-
|
|
19
|
-
// Shared types
|
|
20
|
-
parts.push(`export type RelationStrategy = "query" | "join";
|
|
21
|
-
|
|
22
|
-
export type NumberWithAggregatesFilter = {
|
|
23
|
-
equals?: number;
|
|
24
|
-
not?: number;
|
|
25
|
-
lt?: number;
|
|
26
|
-
lte?: number;
|
|
27
|
-
gt?: number;
|
|
28
|
-
gte?: number;
|
|
29
|
-
};
|
|
30
|
-
`);
|
|
31
|
-
|
|
32
|
-
// Generate Select + Include + Args for each model
|
|
33
|
-
for (const model of schema.models) {
|
|
34
|
-
parts.push(generateSelectType({ model, schema }));
|
|
35
|
-
parts.push(generateIncludeType({ model, schema }));
|
|
36
|
-
parts.push(generateOperationArgs({ model }));
|
|
37
|
-
parts.push(generateAggregateInputTypes({ model }));
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return parts.join("\n");
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// ─── Select Type ──────────────────────────────────────────────────
|
|
44
|
-
|
|
45
|
-
function generateSelectType(params: {
|
|
46
|
-
model: Model;
|
|
47
|
-
schema: Schema;
|
|
48
|
-
}): string {
|
|
49
|
-
const { model, schema } = params;
|
|
50
|
-
|
|
51
|
-
const entries: string[] = [];
|
|
52
|
-
|
|
53
|
-
for (const field of model.fields) {
|
|
54
|
-
if (field.kind === "scalar" || field.kind === "enum") {
|
|
55
|
-
entries.push(` ${field.name}?: boolean;`);
|
|
56
|
-
} else if (field.kind === "relation") {
|
|
57
|
-
if (field.isList) {
|
|
58
|
-
entries.push(` ${field.name}?: boolean | ${field.relatedModel}FindManyArgs;`);
|
|
59
|
-
} else {
|
|
60
|
-
entries.push(` ${field.name}?: boolean | ${field.relatedModel}FindFirstArgs;`);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Add _count support for relation counting
|
|
66
|
-
const countableRelations = model.fields.filter(
|
|
67
|
-
(f): f is RelationField => f.kind === "relation" && f.isList
|
|
68
|
-
);
|
|
69
|
-
if (countableRelations.length > 0) {
|
|
70
|
-
const countFields = countableRelations.map((f) => ` ${f.name}?: boolean;`);
|
|
71
|
-
entries.push(` _count?: boolean | {\n select?: {\n${countFields.join("\n")}\n };\n };`);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return `export type ${model.name}Select = {
|
|
75
|
-
${entries.join("\n")}
|
|
76
|
-
};
|
|
77
|
-
`;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// ─── Include Type ─────────────────────────────────────────────────
|
|
81
|
-
|
|
82
|
-
function generateIncludeType(params: {
|
|
83
|
-
model: Model;
|
|
84
|
-
schema: Schema;
|
|
85
|
-
}): string {
|
|
86
|
-
const { model } = params;
|
|
87
|
-
|
|
88
|
-
const relationFields = model.fields.filter(
|
|
89
|
-
(f): f is RelationField => f.kind === "relation"
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
if (relationFields.length === 0) {
|
|
93
|
-
return `export type ${model.name}Include = Record<string, never>;\n`;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const entries = relationFields.map((f) => {
|
|
97
|
-
if (f.isList) {
|
|
98
|
-
return ` ${f.name}?: boolean | ${f.relatedModel}FindManyArgs;`;
|
|
99
|
-
}
|
|
100
|
-
return ` ${f.name}?: boolean | ${f.relatedModel}FindFirstArgs;`;
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
// Add _count support for relation counting
|
|
104
|
-
const countableRelations = relationFields.filter((f) => f.isList);
|
|
105
|
-
if (countableRelations.length > 0) {
|
|
106
|
-
const countFields = countableRelations.map((f) => ` ${f.name}?: boolean;`);
|
|
107
|
-
entries.push(` _count?: boolean | {\n select?: {\n${countFields.join("\n")}\n };\n };`);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return `export type ${model.name}Include = {
|
|
111
|
-
${entries.join("\n")}
|
|
112
|
-
};
|
|
113
|
-
`;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// ─── Operation Args ───────────────────────────────────────────────
|
|
117
|
-
|
|
118
|
-
function generateOperationArgs(params: { model: Model }): string {
|
|
119
|
-
const { model } = params;
|
|
120
|
-
|
|
121
|
-
// Omit type — inverse of select (exclude specific fields)
|
|
122
|
-
const scalarFieldNames = model.fields
|
|
123
|
-
.filter((f) => f.kind === "scalar" || f.kind === "enum")
|
|
124
|
-
.map((f) => ` ${f.name}?: boolean;`);
|
|
125
|
-
|
|
126
|
-
const omitTypeDef = `export type ${model.name}Omit = {
|
|
127
|
-
${scalarFieldNames.join("\n")}
|
|
128
|
-
};
|
|
129
|
-
`;
|
|
130
|
-
|
|
131
|
-
return `${omitTypeDef}
|
|
132
|
-
export type ${model.name}FindManyArgs = {
|
|
133
|
-
select?: ${model.name}Select | null;
|
|
134
|
-
include?: ${model.name}Include | null;
|
|
135
|
-
omit?: ${model.name}Omit;
|
|
136
|
-
where?: ${model.name}WhereInput;
|
|
137
|
-
orderBy?: ${model.name}OrderByInput | ${model.name}OrderByInput[];
|
|
138
|
-
take?: number;
|
|
139
|
-
skip?: number;
|
|
140
|
-
cursor?: ${model.name}WhereUniqueInput;
|
|
141
|
-
distinct?: (keyof ${model.name}Select)[];
|
|
142
|
-
relationStrategy?: RelationStrategy;
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
export type ${model.name}FindFirstArgs = {
|
|
146
|
-
select?: ${model.name}Select | null;
|
|
147
|
-
include?: ${model.name}Include | null;
|
|
148
|
-
omit?: ${model.name}Omit;
|
|
149
|
-
where?: ${model.name}WhereInput;
|
|
150
|
-
orderBy?: ${model.name}OrderByInput | ${model.name}OrderByInput[];
|
|
151
|
-
take?: number;
|
|
152
|
-
skip?: number;
|
|
153
|
-
cursor?: ${model.name}WhereUniqueInput;
|
|
154
|
-
relationStrategy?: RelationStrategy;
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
export type ${model.name}FindUniqueArgs = {
|
|
158
|
-
select?: ${model.name}Select | null;
|
|
159
|
-
include?: ${model.name}Include | null;
|
|
160
|
-
omit?: ${model.name}Omit;
|
|
161
|
-
where: ${model.name}WhereUniqueInput;
|
|
162
|
-
relationStrategy?: RelationStrategy;
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
export type ${model.name}CreateArgs = {
|
|
166
|
-
select?: ${model.name}Select | null;
|
|
167
|
-
include?: ${model.name}Include | null;
|
|
168
|
-
omit?: ${model.name}Omit;
|
|
169
|
-
data: ${model.name}CreateInput;
|
|
170
|
-
relationStrategy?: RelationStrategy;
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
export type ${model.name}UpdateArgs = {
|
|
174
|
-
select?: ${model.name}Select | null;
|
|
175
|
-
include?: ${model.name}Include | null;
|
|
176
|
-
omit?: ${model.name}Omit;
|
|
177
|
-
where: ${model.name}WhereUniqueInput;
|
|
178
|
-
data: ${model.name}UpdateInput;
|
|
179
|
-
relationStrategy?: RelationStrategy;
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
export type ${model.name}UpsertArgs = {
|
|
183
|
-
select?: ${model.name}Select | null;
|
|
184
|
-
include?: ${model.name}Include | null;
|
|
185
|
-
where: ${model.name}WhereUniqueInput;
|
|
186
|
-
create: ${model.name}CreateInput;
|
|
187
|
-
update: ${model.name}UpdateInput;
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
export type ${model.name}DeleteArgs = {
|
|
191
|
-
select?: ${model.name}Select | null;
|
|
192
|
-
include?: ${model.name}Include | null;
|
|
193
|
-
where: ${model.name}WhereUniqueInput;
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
export type ${model.name}DeleteManyArgs = {
|
|
197
|
-
where?: ${model.name}WhereInput;
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
export type ${model.name}CountArgs = {
|
|
201
|
-
where?: ${model.name}WhereInput;
|
|
202
|
-
orderBy?: ${model.name}OrderByInput | ${model.name}OrderByInput[];
|
|
203
|
-
take?: number;
|
|
204
|
-
skip?: number;
|
|
205
|
-
cursor?: ${model.name}WhereUniqueInput;
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
export type ${model.name}CreateManyArgs = {
|
|
209
|
-
data: ${model.name}CreateInput | ${model.name}CreateInput[];
|
|
210
|
-
skipDuplicates?: boolean;
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
export type ${model.name}CreateManyAndReturnArgs = {
|
|
214
|
-
select?: ${model.name}Select | null;
|
|
215
|
-
include?: ${model.name}Include | null;
|
|
216
|
-
data: ${model.name}CreateInput | ${model.name}CreateInput[];
|
|
217
|
-
skipDuplicates?: boolean;
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
export type ${model.name}UpdateManyArgs = {
|
|
221
|
-
where?: ${model.name}WhereInput;
|
|
222
|
-
data: ${model.name}UpdateInput;
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
export type ${model.name}AggregateArgs = {
|
|
226
|
-
where?: ${model.name}WhereInput;
|
|
227
|
-
orderBy?: ${model.name}OrderByInput | ${model.name}OrderByInput[];
|
|
228
|
-
take?: number;
|
|
229
|
-
skip?: number;
|
|
230
|
-
cursor?: ${model.name}WhereUniqueInput;
|
|
231
|
-
_count?: true | ${model.name}CountAggregateInputType;
|
|
232
|
-
_avg?: ${model.name}AvgAggregateInputType;
|
|
233
|
-
_sum?: ${model.name}SumAggregateInputType;
|
|
234
|
-
_min?: ${model.name}MinAggregateInputType;
|
|
235
|
-
_max?: ${model.name}MaxAggregateInputType;
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
export type ${model.name}GroupByArgs = {
|
|
239
|
-
by: (keyof ${model.name}Select)[];
|
|
240
|
-
where?: ${model.name}WhereInput;
|
|
241
|
-
orderBy?: ${model.name}OrderByInput | ${model.name}OrderByInput[];
|
|
242
|
-
having?: ${model.name}ScalarWhereWithAggregatesInput;
|
|
243
|
-
take?: number;
|
|
244
|
-
skip?: number;
|
|
245
|
-
_count?: true | ${model.name}CountAggregateInputType;
|
|
246
|
-
_avg?: ${model.name}AvgAggregateInputType;
|
|
247
|
-
_sum?: ${model.name}SumAggregateInputType;
|
|
248
|
-
_min?: ${model.name}MinAggregateInputType;
|
|
249
|
-
_max?: ${model.name}MaxAggregateInputType;
|
|
250
|
-
};
|
|
251
|
-
`;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// ─── Aggregate Input Types ────────────────────────────────────────
|
|
255
|
-
|
|
256
|
-
const NUMERIC_PRISMA_TYPES = new Set(["Int", "Float", "Decimal", "BigInt"]);
|
|
257
|
-
|
|
258
|
-
function generateAggregateInputTypes(params: { model: Model }): string {
|
|
259
|
-
const { model } = params;
|
|
260
|
-
|
|
261
|
-
const scalarFields = model.fields.filter(
|
|
262
|
-
(f): f is ScalarField => f.kind === "scalar"
|
|
263
|
-
);
|
|
264
|
-
|
|
265
|
-
const numericFields = scalarFields.filter(
|
|
266
|
-
(f) => NUMERIC_PRISMA_TYPES.has(f.prismaType)
|
|
267
|
-
);
|
|
268
|
-
|
|
269
|
-
// CountAggregateInputType — all scalar fields + _all
|
|
270
|
-
const countEntries = [
|
|
271
|
-
` _all?: boolean;`,
|
|
272
|
-
...scalarFields.map((f) => ` ${f.name}?: boolean;`),
|
|
273
|
-
];
|
|
274
|
-
|
|
275
|
-
// AvgAggregateInputType — only numeric fields
|
|
276
|
-
const avgEntries = numericFields.map((f) => ` ${f.name}?: boolean;`);
|
|
277
|
-
|
|
278
|
-
// SumAggregateInputType — only numeric fields
|
|
279
|
-
const sumEntries = numericFields.map((f) => ` ${f.name}?: boolean;`);
|
|
280
|
-
|
|
281
|
-
// MinAggregateInputType — all scalar fields
|
|
282
|
-
const minEntries = scalarFields.map((f) => ` ${f.name}?: boolean;`);
|
|
283
|
-
|
|
284
|
-
// MaxAggregateInputType — all scalar fields
|
|
285
|
-
const maxEntries = scalarFields.map((f) => ` ${f.name}?: boolean;`);
|
|
286
|
-
|
|
287
|
-
// ScalarWhereWithAggregatesInput — for HAVING clause in groupBy
|
|
288
|
-
const havingEntries = scalarFields.map((f) => {
|
|
289
|
-
return ` ${f.name}?: {
|
|
290
|
-
_count?: NumberWithAggregatesFilter;
|
|
291
|
-
_avg?: NumberWithAggregatesFilter;
|
|
292
|
-
_sum?: NumberWithAggregatesFilter;
|
|
293
|
-
_min?: NumberWithAggregatesFilter;
|
|
294
|
-
_max?: NumberWithAggregatesFilter;
|
|
295
|
-
};`;
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
return `export type ${model.name}CountAggregateInputType = {
|
|
299
|
-
${countEntries.join("\n")}
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
export type ${model.name}AvgAggregateInputType = {
|
|
303
|
-
${avgEntries.length > 0 ? avgEntries.join("\n") : " [key: string]: never;"}
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
export type ${model.name}SumAggregateInputType = {
|
|
307
|
-
${sumEntries.length > 0 ? sumEntries.join("\n") : " [key: string]: never;"}
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
export type ${model.name}MinAggregateInputType = {
|
|
311
|
-
${minEntries.join("\n")}
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
export type ${model.name}MaxAggregateInputType = {
|
|
315
|
-
${maxEntries.join("\n")}
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
export type ${model.name}ScalarWhereWithAggregatesInput = {
|
|
319
|
-
${havingEntries.join("\n")}
|
|
320
|
-
};
|
|
321
|
-
`;
|
|
322
|
-
}
|
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
import type { Model, Schema } from "@vibeorm/parser";
|
|
2
|
-
import { fileHeader, toCamelCase } from "../utils.ts";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Generates the main index.ts file that:
|
|
6
|
-
* - Re-exports all types
|
|
7
|
-
* - Creates the VibeClient class that wires delegates to the runtime
|
|
8
|
-
*/
|
|
9
|
-
export function generateClient(params: { schema: Schema }): string {
|
|
10
|
-
const { schema } = params;
|
|
11
|
-
const parts: string[] = [fileHeader()];
|
|
12
|
-
|
|
13
|
-
// Re-exports
|
|
14
|
-
parts.push(`export * from "./enums.ts";`);
|
|
15
|
-
parts.push(`export * from "./models.ts";`);
|
|
16
|
-
parts.push(`export * from "./inputs.ts";`);
|
|
17
|
-
parts.push(`export * from "./args.ts";`);
|
|
18
|
-
parts.push(`export * from "./result.ts";`);
|
|
19
|
-
parts.push(`export * from "./delegates.ts";`);
|
|
20
|
-
parts.push(`export * from "./schemas.ts";`);
|
|
21
|
-
parts.push(`export * from "./view-types.ts";`);
|
|
22
|
-
parts.push(``);
|
|
23
|
-
|
|
24
|
-
// Import delegate types
|
|
25
|
-
const delegateImports = schema.models
|
|
26
|
-
.map((m) => `${m.name}Delegate`)
|
|
27
|
-
.join(", ");
|
|
28
|
-
parts.push(`import type { ${delegateImports} } from "./delegates.ts";`);
|
|
29
|
-
|
|
30
|
-
// Import view types
|
|
31
|
-
parts.push(`import type { ViewDefinitionInput, ViewClient } from "./view-types.ts";`);
|
|
32
|
-
|
|
33
|
-
// Import runtime
|
|
34
|
-
parts.push(`import { createClient } from "@vibeorm/runtime";`);
|
|
35
|
-
parts.push(`import type { VibeClientOptions, TransactionOptions } from "@vibeorm/runtime";`);
|
|
36
|
-
parts.push(``);
|
|
37
|
-
|
|
38
|
-
// Import schemas for validation wiring
|
|
39
|
-
const schemaImports = schema.models.flatMap((m) => [
|
|
40
|
-
`${m.name}Schema`,
|
|
41
|
-
`${m.name}CreateInputSchema`,
|
|
42
|
-
`${m.name}UpdateInputSchema`,
|
|
43
|
-
`${m.name}WhereInputSchema`,
|
|
44
|
-
]);
|
|
45
|
-
parts.push(`import { ${schemaImports.join(", ")} } from "./schemas.ts";`);
|
|
46
|
-
parts.push(``);
|
|
47
|
-
|
|
48
|
-
// Generate the model metadata object that the runtime needs
|
|
49
|
-
parts.push(generateModelMeta({ schema }));
|
|
50
|
-
|
|
51
|
-
// Generate the schemas map for validation wiring
|
|
52
|
-
parts.push(generateSchemasMap({ schema }));
|
|
53
|
-
|
|
54
|
-
// Generate the VibeClient type (intersection of all delegates)
|
|
55
|
-
const clientProperties = schema.models
|
|
56
|
-
.map((m) => generateClientProperty({ model: m }))
|
|
57
|
-
.join("\n");
|
|
58
|
-
|
|
59
|
-
parts.push(`export type VibeClientInstance = {
|
|
60
|
-
${clientProperties}
|
|
61
|
-
$transaction<T>(fn: (tx: VibeClientInstance) => Promise<T>, options?: TransactionOptions): Promise<T>;
|
|
62
|
-
$transaction(promises: Promise<unknown>[], options?: TransactionOptions): Promise<unknown[]>;
|
|
63
|
-
$queryRaw<T = unknown>(strings: TemplateStringsArray, ...values: unknown[]): Promise<T[]>;
|
|
64
|
-
$executeRaw(strings: TemplateStringsArray, ...values: unknown[]): Promise<number>;
|
|
65
|
-
$queryRawUnsafe<T = unknown>(query: string, ...values: unknown[]): Promise<T[]>;
|
|
66
|
-
$executeRawUnsafe(query: string, ...values: unknown[]): Promise<number>;
|
|
67
|
-
$connect(): Promise<void>;
|
|
68
|
-
$disconnect(): Promise<void>;
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Create a read-only view that restricts which fields are returned per model.
|
|
72
|
-
*
|
|
73
|
-
* @example
|
|
74
|
-
* const publicApi = db.defineView({
|
|
75
|
-
* name: "public-api",
|
|
76
|
-
* definition: {
|
|
77
|
-
* user: { id: true, name: true, createdAt: true },
|
|
78
|
-
* post: { id: true, title: true },
|
|
79
|
-
* },
|
|
80
|
-
* });
|
|
81
|
-
*
|
|
82
|
-
* // Only returns { id, name, createdAt }
|
|
83
|
-
* const users = await publicApi.user.findMany();
|
|
84
|
-
*
|
|
85
|
-
* // Reveal extra fields (accessible but excluded from JSON.stringify)
|
|
86
|
-
* const user = await publicApi.user.findUniqueOrThrow({
|
|
87
|
-
* where: { id: 1 },
|
|
88
|
-
* reveal: { email: true },
|
|
89
|
-
* });
|
|
90
|
-
* user.email; // accessible
|
|
91
|
-
* JSON.stringify(user); // { id, name, createdAt } — email excluded
|
|
92
|
-
* user.$unsafe(); // { id, name, createdAt, email } — escape hatch
|
|
93
|
-
*/
|
|
94
|
-
defineView<D extends ViewDefinitionInput>(params: {
|
|
95
|
-
name: string;
|
|
96
|
-
definition: D;
|
|
97
|
-
}): ViewClient<D>;
|
|
98
|
-
};
|
|
99
|
-
`);
|
|
100
|
-
|
|
101
|
-
// Generate the VibeClient constructor function
|
|
102
|
-
parts.push(`export function VibeClient(options: VibeClientOptions): VibeClientInstance {
|
|
103
|
-
return createClient({
|
|
104
|
-
options,
|
|
105
|
-
modelMeta: MODEL_META,
|
|
106
|
-
schemas: options.validate ? SCHEMAS : undefined,
|
|
107
|
-
}) as unknown as VibeClientInstance;
|
|
108
|
-
}
|
|
109
|
-
`);
|
|
110
|
-
|
|
111
|
-
return parts.join("\n");
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function generateClientProperty(params: { model: Model }): string {
|
|
115
|
-
const { model } = params;
|
|
116
|
-
const modelVar = toCamelCase({ str: model.name });
|
|
117
|
-
|
|
118
|
-
return ` /**
|
|
119
|
-
* Operations for the ${model.name} model.
|
|
120
|
-
*
|
|
121
|
-
* @example
|
|
122
|
-
* const rows = await db.${modelVar}.findMany();
|
|
123
|
-
*
|
|
124
|
-
* @example
|
|
125
|
-
* const filteredRows = await db.${modelVar}.findMany({
|
|
126
|
-
* where: {
|
|
127
|
-
* // Add ${model.name}-specific filters here
|
|
128
|
-
* },
|
|
129
|
-
* });
|
|
130
|
-
*
|
|
131
|
-
* @example
|
|
132
|
-
* const page = await db.${modelVar}.findMany({
|
|
133
|
-
* where: {
|
|
134
|
-
* // Add ${model.name}-specific filters here
|
|
135
|
-
* },
|
|
136
|
-
* orderBy: {
|
|
137
|
-
* // e.g. createdAt: "desc"
|
|
138
|
-
* },
|
|
139
|
-
* take: 20,
|
|
140
|
-
* skip: 0,
|
|
141
|
-
* });
|
|
142
|
-
*/
|
|
143
|
-
${modelVar}: ${model.name}Delegate;`;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function generateModelMeta(params: { schema: Schema }): string {
|
|
147
|
-
const { schema } = params;
|
|
148
|
-
|
|
149
|
-
const modelEntries = schema.models.map((model) => {
|
|
150
|
-
const scalarFields = model.fields
|
|
151
|
-
.filter((f) => f.kind === "scalar" || f.kind === "enum")
|
|
152
|
-
.map((f) => {
|
|
153
|
-
const dbName = f.kind === "scalar" ? f.dbName : f.dbName;
|
|
154
|
-
const isUpdatedAt = f.kind === "scalar" && f.isUpdatedAt;
|
|
155
|
-
const extras: string[] = [];
|
|
156
|
-
if (isUpdatedAt) extras.push(`isUpdatedAt: true`);
|
|
157
|
-
if (f.kind === "scalar") extras.push(`type: "${f.prismaType}"`);
|
|
158
|
-
if (f.isList) extras.push(`isList: true`);
|
|
159
|
-
|
|
160
|
-
// Emit default kind for application-level generation (uuid, cuid, nanoid, ulid)
|
|
161
|
-
if (f.default) {
|
|
162
|
-
const appLevelDefaults = new Set(["uuid", "cuid", "nanoid", "ulid"]);
|
|
163
|
-
if (appLevelDefaults.has(f.default.kind)) {
|
|
164
|
-
extras.push(`hasDefault: "${f.default.kind}"`);
|
|
165
|
-
} else if (f.default.kind !== "dbgenerated") {
|
|
166
|
-
// autoincrement, now, literal — DB handles these, but runtime should know
|
|
167
|
-
extras.push(`hasDefault: true`);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Extract @vibeorm.idPrefix from documentation comment
|
|
172
|
-
const doc = (f.kind === "scalar" || f.kind === "enum") ? f.documentation : undefined;
|
|
173
|
-
if (doc) {
|
|
174
|
-
const prefixMatch = doc.match(/@vibeorm\.idPrefix\(["']([^"']+)["']\)/);
|
|
175
|
-
if (prefixMatch) {
|
|
176
|
-
extras.push(`idPrefix: "${prefixMatch[1]}"`);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const extraStr = extras.length > 0 ? `, ${extras.join(", ")}` : "";
|
|
181
|
-
return ` { name: "${f.name}", dbName: "${dbName}", kind: "${f.kind}" as const${extraStr} }`;
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
const relationFields = model.fields
|
|
185
|
-
.filter((f) => f.kind === "relation")
|
|
186
|
-
.map((f) => {
|
|
187
|
-
if (f.kind !== "relation") return "";
|
|
188
|
-
const isM2M = f.relation.type === "manyToMany";
|
|
189
|
-
let joinTableLine = "";
|
|
190
|
-
if (isM2M) {
|
|
191
|
-
const sorted = [model.name, f.relatedModel].sort();
|
|
192
|
-
const jtName = `_${sorted[0]}To${sorted[1]}`;
|
|
193
|
-
joinTableLine = `\n joinTable: "${jtName}",`;
|
|
194
|
-
}
|
|
195
|
-
const relationNameLine = f.relation.name
|
|
196
|
-
? `\n relationName: "${f.relation.name}",`
|
|
197
|
-
: "";
|
|
198
|
-
return ` {
|
|
199
|
-
name: "${f.name}",
|
|
200
|
-
kind: "relation" as const,
|
|
201
|
-
relatedModel: "${f.relatedModel}",
|
|
202
|
-
type: "${f.relation.type}" as const,
|
|
203
|
-
isList: ${f.isList},
|
|
204
|
-
isForeignKey: ${f.relation.isForeignKey},
|
|
205
|
-
fields: ${JSON.stringify(f.relation.fields)},
|
|
206
|
-
references: ${JSON.stringify(f.relation.references)},${relationNameLine}${joinTableLine}
|
|
207
|
-
}`;
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
const pkFields = JSON.stringify(model.primaryKey.fields);
|
|
211
|
-
const uniqueFields = model.fields
|
|
212
|
-
.filter((f) => (f.kind === "scalar" || f.kind === "enum") && f.isUnique)
|
|
213
|
-
.map((f) => `"${f.name}"`);
|
|
214
|
-
|
|
215
|
-
return ` ${toCamelCase({ str: model.name })}: {
|
|
216
|
-
name: "${model.name}",
|
|
217
|
-
dbName: "${model.dbName}",
|
|
218
|
-
primaryKey: ${pkFields},
|
|
219
|
-
uniqueFields: [${uniqueFields.join(", ")}],
|
|
220
|
-
scalarFields: [
|
|
221
|
-
${scalarFields.join(",\n")}
|
|
222
|
-
],
|
|
223
|
-
relationFields: [
|
|
224
|
-
${relationFields.join(",\n")}
|
|
225
|
-
],
|
|
226
|
-
}`;
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
return `const MODEL_META = {
|
|
230
|
-
${modelEntries.join(",\n")}
|
|
231
|
-
} as const;
|
|
232
|
-
`;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function generateSchemasMap(params: { schema: Schema }): string {
|
|
236
|
-
const { schema } = params;
|
|
237
|
-
|
|
238
|
-
const entries = schema.models.map((model) => {
|
|
239
|
-
const key = toCamelCase({ str: model.name });
|
|
240
|
-
return ` ${key}: {
|
|
241
|
-
model: ${model.name}Schema,
|
|
242
|
-
createInput: ${model.name}CreateInputSchema,
|
|
243
|
-
updateInput: ${model.name}UpdateInputSchema,
|
|
244
|
-
whereInput: ${model.name}WhereInputSchema,
|
|
245
|
-
}`;
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
return `const SCHEMAS = {
|
|
249
|
-
${entries.join(",\n")}
|
|
250
|
-
};
|
|
251
|
-
`;
|
|
252
|
-
}
|