@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
package/dist/index.js
ADDED
|
@@ -0,0 +1,1521 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/generator/src/generate.ts
|
|
3
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { buildComputedManifest, validateComputedConfig } from "@vibeorm/schema";
|
|
6
|
+
|
|
7
|
+
// packages/generator/src/emit-dts.ts
|
|
8
|
+
import { VibeError as VibeError3 } from "@vibeorm/schema";
|
|
9
|
+
|
|
10
|
+
// packages/generator/src/extension-bake.ts
|
|
11
|
+
import {
|
|
12
|
+
VibeError,
|
|
13
|
+
buildExtensionRegistry,
|
|
14
|
+
extensionDeclarations,
|
|
15
|
+
extensionManifestEntry,
|
|
16
|
+
providerToDialect
|
|
17
|
+
} from "@vibeorm/schema";
|
|
18
|
+
var EMPTY_BAKED_EXTENSIONS = {
|
|
19
|
+
jsImports: [],
|
|
20
|
+
mounts: [],
|
|
21
|
+
manifest: [],
|
|
22
|
+
dtsImports: [],
|
|
23
|
+
delegateDts: new Map,
|
|
24
|
+
moduleDts: [],
|
|
25
|
+
clientDts: [],
|
|
26
|
+
empty: true
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
class ImportCollector {
|
|
30
|
+
byModule = new Map;
|
|
31
|
+
add(specs) {
|
|
32
|
+
for (const spec of specs ?? []) {
|
|
33
|
+
const names = this.byModule.get(spec.from) ?? new Set;
|
|
34
|
+
for (const name of spec.names)
|
|
35
|
+
names.add(name);
|
|
36
|
+
this.byModule.set(spec.from, names);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
lines(params) {
|
|
40
|
+
return [...this.byModule.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([from, names]) => `import ${params.typeOnly ? "type " : ""}{ ${[...names].sort().join(", ")} } from ${JSON.stringify(from)};`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function indent(lines) {
|
|
44
|
+
return lines.map((line) => line.length === 0 ? line : ` ${line}`);
|
|
45
|
+
}
|
|
46
|
+
function bakeExtensions(params) {
|
|
47
|
+
const { schema } = params;
|
|
48
|
+
if (params.extensions.length === 0)
|
|
49
|
+
return EMPTY_BAKED_EXTENSIONS;
|
|
50
|
+
buildExtensionRegistry({ extensions: params.extensions });
|
|
51
|
+
const dialect = providerToDialect({ provider: schema.datasource.provider });
|
|
52
|
+
const provider = schema.datasource.provider;
|
|
53
|
+
const sorted = [...params.extensions].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
54
|
+
const jsImports = new ImportCollector;
|
|
55
|
+
const dtsImports = new ImportCollector;
|
|
56
|
+
const mounts = [];
|
|
57
|
+
const delegateDts = new Map;
|
|
58
|
+
const moduleDts = [];
|
|
59
|
+
const clientDts = [];
|
|
60
|
+
const useContribution = (paramsInner) => {
|
|
61
|
+
const { extension, contribution, scope, model } = paramsInner;
|
|
62
|
+
const property = scope === "model" ? extension.name : `$${extension.name}`;
|
|
63
|
+
jsImports.add(contribution.imports);
|
|
64
|
+
dtsImports.add(contribution.dtsImports);
|
|
65
|
+
moduleDts.push(...contribution.dtsTypes ?? []);
|
|
66
|
+
mounts.push(`{ extension: ${JSON.stringify(extension.name)}, scope: ${JSON.stringify(scope)}, ${model === undefined ? "" : `model: ${JSON.stringify(model)}, `}property: ${JSON.stringify(property)}, create: ${contribution.runtimeGlue} }`);
|
|
67
|
+
if (scope === "model" && model !== undefined) {
|
|
68
|
+
const lines = delegateDts.get(model) ?? [];
|
|
69
|
+
lines.push(...indent(contribution.dts));
|
|
70
|
+
delegateDts.set(model, lines);
|
|
71
|
+
} else {
|
|
72
|
+
clientDts.push(...indent(contribution.dts));
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
for (const extension of sorted) {
|
|
76
|
+
const declarations = extensionDeclarations({ schema, name: extension.name });
|
|
77
|
+
const modelHook = extension.client?.model;
|
|
78
|
+
if (modelHook !== undefined) {
|
|
79
|
+
for (const model of schema.models) {
|
|
80
|
+
const declaration = declarations[model.name];
|
|
81
|
+
if (declaration === undefined)
|
|
82
|
+
continue;
|
|
83
|
+
const contribution = modelHook({
|
|
84
|
+
model,
|
|
85
|
+
schema,
|
|
86
|
+
dialect,
|
|
87
|
+
provider,
|
|
88
|
+
options: extension.options,
|
|
89
|
+
declaration,
|
|
90
|
+
typeNames: { payload: model.name, whereInput: `${model.name}WhereInput` }
|
|
91
|
+
});
|
|
92
|
+
if (contribution === null)
|
|
93
|
+
continue;
|
|
94
|
+
const clash = model.fields.some((field) => field.name === extension.name) || model.relations.some((relation) => relation.name === extension.name);
|
|
95
|
+
if (clash) {
|
|
96
|
+
throw new VibeError({
|
|
97
|
+
code: "VIBE_CONFIG",
|
|
98
|
+
message: `extension "${extension.name}" collides with a field/relation named "${extension.name}" on model "${model.name}" \u2014 rename one of them`,
|
|
99
|
+
meta: { extension: extension.name, model: model.name }
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
useContribution({ extension, contribution, scope: "model", model: model.name });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const rootHook = extension.client?.root;
|
|
106
|
+
if (rootHook !== undefined) {
|
|
107
|
+
const contribution = rootHook({
|
|
108
|
+
schema,
|
|
109
|
+
dialect,
|
|
110
|
+
provider,
|
|
111
|
+
options: extension.options,
|
|
112
|
+
declarations
|
|
113
|
+
});
|
|
114
|
+
if (contribution !== null) {
|
|
115
|
+
useContribution({ extension, contribution, scope: "client" });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
jsImports: jsImports.lines({ typeOnly: false }),
|
|
121
|
+
mounts,
|
|
122
|
+
manifest: sorted.map((extension) => extensionManifestEntry({ extension })),
|
|
123
|
+
dtsImports: dtsImports.lines({ typeOnly: true }),
|
|
124
|
+
delegateDts,
|
|
125
|
+
moduleDts,
|
|
126
|
+
clientDts,
|
|
127
|
+
empty: false
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// packages/generator/src/type-names.ts
|
|
132
|
+
var PAYLOAD_TYPES = {
|
|
133
|
+
String: "string",
|
|
134
|
+
Boolean: "boolean",
|
|
135
|
+
Int: "number",
|
|
136
|
+
BigInt: "bigint",
|
|
137
|
+
Float: "number",
|
|
138
|
+
Decimal: "string",
|
|
139
|
+
DateTime: "Date",
|
|
140
|
+
Json: "JsonValue",
|
|
141
|
+
Bytes: "Uint8Array"
|
|
142
|
+
};
|
|
143
|
+
var INPUT_TYPES = {
|
|
144
|
+
String: "string",
|
|
145
|
+
Boolean: "boolean",
|
|
146
|
+
Int: "number",
|
|
147
|
+
BigInt: "bigint | number",
|
|
148
|
+
Float: "number",
|
|
149
|
+
Decimal: "string | number",
|
|
150
|
+
DateTime: "Date | string",
|
|
151
|
+
Json: "InputJsonValue",
|
|
152
|
+
Bytes: "Uint8Array"
|
|
153
|
+
};
|
|
154
|
+
var FILTER_PREFIX = {
|
|
155
|
+
String: "String",
|
|
156
|
+
Boolean: "Bool",
|
|
157
|
+
Int: "Int",
|
|
158
|
+
BigInt: "BigInt",
|
|
159
|
+
Float: "Float",
|
|
160
|
+
Decimal: "Decimal",
|
|
161
|
+
DateTime: "DateTime",
|
|
162
|
+
Json: "Json",
|
|
163
|
+
Bytes: "Bytes"
|
|
164
|
+
};
|
|
165
|
+
var NUMERIC_SCALARS = new Set(["Int", "BigInt", "Float", "Decimal"]);
|
|
166
|
+
var MINMAX_SCALARS = new Set([
|
|
167
|
+
"String",
|
|
168
|
+
"Int",
|
|
169
|
+
"BigInt",
|
|
170
|
+
"Float",
|
|
171
|
+
"Decimal",
|
|
172
|
+
"DateTime"
|
|
173
|
+
]);
|
|
174
|
+
function payloadBaseType(params) {
|
|
175
|
+
const { field } = params;
|
|
176
|
+
return field.enumName ?? PAYLOAD_TYPES[field.scalar];
|
|
177
|
+
}
|
|
178
|
+
function inputBaseType(params) {
|
|
179
|
+
const { field } = params;
|
|
180
|
+
return field.enumName ?? INPUT_TYPES[field.scalar];
|
|
181
|
+
}
|
|
182
|
+
function filterName(params) {
|
|
183
|
+
const { field } = params;
|
|
184
|
+
const prefix = field.enumName ?? FILTER_PREFIX[field.scalar];
|
|
185
|
+
if (field.isList)
|
|
186
|
+
return `${prefix}ListFilter`;
|
|
187
|
+
return field.isOptional ? `${prefix}NullableFilter` : `${prefix}Filter`;
|
|
188
|
+
}
|
|
189
|
+
function updateOpsName(params) {
|
|
190
|
+
const { field } = params;
|
|
191
|
+
if (field.isList || field.scalar === null || !NUMERIC_SCALARS.has(field.scalar))
|
|
192
|
+
return null;
|
|
193
|
+
const prefix = FILTER_PREFIX[field.scalar];
|
|
194
|
+
return field.isOptional ? `Nullable${prefix}FieldUpdateOperationsInput` : `${prefix}FieldUpdateOperationsInput`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// packages/generator/src/util.ts
|
|
198
|
+
import { VibeError as VibeError2 } from "@vibeorm/schema";
|
|
199
|
+
function pascal(params) {
|
|
200
|
+
const { name } = params;
|
|
201
|
+
if (name.length === 0)
|
|
202
|
+
return name;
|
|
203
|
+
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
204
|
+
}
|
|
205
|
+
function clientNameOf(params) {
|
|
206
|
+
const { name } = params;
|
|
207
|
+
if (name.length === 0)
|
|
208
|
+
return name;
|
|
209
|
+
return name.charAt(0).toLowerCase() + name.slice(1);
|
|
210
|
+
}
|
|
211
|
+
var IDENTIFIER_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
212
|
+
function propKey(params) {
|
|
213
|
+
const { name } = params;
|
|
214
|
+
return IDENTIFIER_RE.test(name) ? name : JSON.stringify(name);
|
|
215
|
+
}
|
|
216
|
+
function tsString(params) {
|
|
217
|
+
return JSON.stringify(params.value);
|
|
218
|
+
}
|
|
219
|
+
function maybeParens(params) {
|
|
220
|
+
const { type } = params;
|
|
221
|
+
return type.includes("|") ? `(${type})` : type;
|
|
222
|
+
}
|
|
223
|
+
function stableStringify(params) {
|
|
224
|
+
return renderValue({ value: params.value, indent: "" });
|
|
225
|
+
}
|
|
226
|
+
function renderValue(params) {
|
|
227
|
+
const { value, indent: indent2 } = params;
|
|
228
|
+
if (value === null)
|
|
229
|
+
return "null";
|
|
230
|
+
const kind = typeof value;
|
|
231
|
+
if (kind === "string" || kind === "number" || kind === "boolean")
|
|
232
|
+
return JSON.stringify(value);
|
|
233
|
+
if (Array.isArray(value)) {
|
|
234
|
+
if (value.length === 0)
|
|
235
|
+
return "[]";
|
|
236
|
+
const inner = `${indent2} `;
|
|
237
|
+
const items = value.map((item) => inner + renderValue({ value: item, indent: inner }));
|
|
238
|
+
return `[
|
|
239
|
+
${items.join(`,
|
|
240
|
+
`)}
|
|
241
|
+
${indent2}]`;
|
|
242
|
+
}
|
|
243
|
+
if (kind === "object") {
|
|
244
|
+
const record = value;
|
|
245
|
+
const keys = Object.keys(record).filter((key) => record[key] !== undefined).sort();
|
|
246
|
+
if (keys.length === 0)
|
|
247
|
+
return "{}";
|
|
248
|
+
const inner = `${indent2} `;
|
|
249
|
+
const items = keys.map((key) => `${inner}${JSON.stringify(key)}: ${renderValue({ value: record[key], indent: inner })}`);
|
|
250
|
+
return `{
|
|
251
|
+
${items.join(`,
|
|
252
|
+
`)}
|
|
253
|
+
${indent2}}`;
|
|
254
|
+
}
|
|
255
|
+
throw new VibeError2({
|
|
256
|
+
code: "VIBE_INTERNAL",
|
|
257
|
+
message: `internal: cannot serialize a ${kind} value into the generated client`,
|
|
258
|
+
meta: { kind }
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// packages/generator/src/emit-dts.ts
|
|
263
|
+
function banner(params) {
|
|
264
|
+
return `// \u2500\u2500\u2500 ${params.title} \u2500\u2500\u2500`;
|
|
265
|
+
}
|
|
266
|
+
function objectAlias(params) {
|
|
267
|
+
const { name, lines } = params;
|
|
268
|
+
if (lines.length === 0)
|
|
269
|
+
return `export type ${name} = Record<string, never>;`;
|
|
270
|
+
return `export type ${name} = {
|
|
271
|
+
${lines.join(`
|
|
272
|
+
`)}
|
|
273
|
+
};`;
|
|
274
|
+
}
|
|
275
|
+
function orMany(params) {
|
|
276
|
+
const { type } = params;
|
|
277
|
+
return `${type} | readonly ${maybeParens({ type })}[]`;
|
|
278
|
+
}
|
|
279
|
+
function payloadFieldType(params) {
|
|
280
|
+
const { field } = params;
|
|
281
|
+
const base = payloadBaseType({ field });
|
|
282
|
+
if (field.isList)
|
|
283
|
+
return `${maybeParens({ type: base })}[]`;
|
|
284
|
+
return field.isOptional ? `${base} | null` : base;
|
|
285
|
+
}
|
|
286
|
+
function relationPayloadType(params) {
|
|
287
|
+
const { relation } = params;
|
|
288
|
+
if (relation.isList)
|
|
289
|
+
return `${relation.target}[]`;
|
|
290
|
+
return relation.isOptional ? `${relation.target} | null` : relation.target;
|
|
291
|
+
}
|
|
292
|
+
var PREAMBLE = `${banner({ title: "Shared scalar & helper types" })}
|
|
293
|
+
|
|
294
|
+
/** JSON as stored/returned by Json columns (decoded payload shape). */
|
|
295
|
+
export type JsonValue = string | number | boolean | null | { [key: string]: JsonValue } | JsonValue[];
|
|
296
|
+
|
|
297
|
+
/** Object form accepted as Json INPUT \u2014 readonly, and undefined-valued keys are dropped on serialize. */
|
|
298
|
+
export type InputJsonObject = { readonly [key: string]: InputJsonValue | null | undefined };
|
|
299
|
+
|
|
300
|
+
/** Array form accepted as Json INPUT \u2014 readonly arrays are welcome (v1 lesson, LEARNINGS bug 7). */
|
|
301
|
+
export type InputJsonArray = ReadonlyArray<InputJsonValue | null>;
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* JSON as ACCEPTED for Json input positions \u2014 deliberately wider than
|
|
305
|
+
* JsonValue: readonly containers and toJSON-bearing values (Date) serialize
|
|
306
|
+
* fine, the runtime stringifies with JSON.stringify (v1 lessons, bugs 7+8).
|
|
307
|
+
*/
|
|
308
|
+
export type InputJsonValue = string | number | boolean | InputJsonObject | InputJsonArray | { toJSON(): unknown };
|
|
309
|
+
|
|
310
|
+
/** \`strict\` (board #20) forces case-SENSITIVE matching where the engine folds (sqlite/mysql); a no-op on postgres. */
|
|
311
|
+
export type QueryMode = "default" | "insensitive" | "strict";
|
|
312
|
+
|
|
313
|
+
export type SortOrder = "asc" | "desc";
|
|
314
|
+
|
|
315
|
+
export type SortOrderWithNulls = SortOrder | { readonly sort: SortOrder; readonly nulls?: "first" | "last" };
|
|
316
|
+
|
|
317
|
+
/** How relations are loaded: batched WHERE-IN queries, or a single JOIN (pg/pglite). */
|
|
318
|
+
export type RelationStrategy = "query" | "join";
|
|
319
|
+
|
|
320
|
+
/** Result of createMany/updateMany/deleteMany. */
|
|
321
|
+
export type BatchPayload = { readonly count: number };
|
|
322
|
+
|
|
323
|
+
/** Keys of an omit object whose value is exactly true \u2014 the scalars the result drops. */
|
|
324
|
+
type OmittedKeys<O> = keyof { [K in keyof O as O[K] extends true ? K : never]: 0 };`;
|
|
325
|
+
var SCALAR_FILTER_CONFIGS = [
|
|
326
|
+
{ prefix: "String", value: "string", comparable: true, text: true },
|
|
327
|
+
{ prefix: "Int", value: "number", comparable: true, text: false },
|
|
328
|
+
{ prefix: "Float", value: "number", comparable: true, text: false },
|
|
329
|
+
{ prefix: "BigInt", value: "bigint | number", comparable: true, text: false },
|
|
330
|
+
{ prefix: "Decimal", value: "string | number", comparable: true, text: false },
|
|
331
|
+
{ prefix: "DateTime", value: "Date | string", comparable: true, text: false },
|
|
332
|
+
{ prefix: "Bool", value: "boolean", comparable: false, text: false },
|
|
333
|
+
{ prefix: "Bytes", value: "Uint8Array", comparable: false, text: false }
|
|
334
|
+
];
|
|
335
|
+
function scalarFilterAlias(params) {
|
|
336
|
+
const { config, nullable } = params;
|
|
337
|
+
const name = nullable ? `${config.prefix}NullableFilter` : `${config.prefix}Filter`;
|
|
338
|
+
const value = config.value;
|
|
339
|
+
const valueOrNull = nullable ? `${value} | null` : value;
|
|
340
|
+
const array = `readonly ${maybeParens({ type: value })}[]`;
|
|
341
|
+
const lines = [];
|
|
342
|
+
lines.push(` readonly equals?: ${valueOrNull};`);
|
|
343
|
+
lines.push(` readonly in?: ${array};`);
|
|
344
|
+
lines.push(` readonly notIn?: ${array};`);
|
|
345
|
+
if (config.comparable) {
|
|
346
|
+
lines.push(` readonly lt?: ${value};`);
|
|
347
|
+
lines.push(` readonly lte?: ${value};`);
|
|
348
|
+
lines.push(` readonly gt?: ${value};`);
|
|
349
|
+
lines.push(` readonly gte?: ${value};`);
|
|
350
|
+
}
|
|
351
|
+
if (config.text) {
|
|
352
|
+
lines.push(` readonly contains?: ${value};`);
|
|
353
|
+
lines.push(` readonly startsWith?: ${value};`);
|
|
354
|
+
lines.push(` readonly endsWith?: ${value};`);
|
|
355
|
+
lines.push(` readonly mode?: QueryMode;`);
|
|
356
|
+
}
|
|
357
|
+
lines.push(` readonly not?: ${value} | ${name}${nullable ? " | null" : ""};`);
|
|
358
|
+
return objectAlias({ name, lines });
|
|
359
|
+
}
|
|
360
|
+
function scalarListFilterAlias(params) {
|
|
361
|
+
const { config } = params;
|
|
362
|
+
const value = config.value;
|
|
363
|
+
const array = `readonly ${maybeParens({ type: value })}[]`;
|
|
364
|
+
return objectAlias({
|
|
365
|
+
name: `${config.prefix}ListFilter`,
|
|
366
|
+
lines: [
|
|
367
|
+
` readonly equals?: ${array} | null;`,
|
|
368
|
+
` readonly has?: ${value} | null;`,
|
|
369
|
+
` readonly hasEvery?: ${array};`,
|
|
370
|
+
` readonly hasSome?: ${array};`,
|
|
371
|
+
` readonly isEmpty?: boolean;`
|
|
372
|
+
]
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
var UPDATE_OPS_CONFIGS = [
|
|
376
|
+
{ prefix: "Int", value: "number" },
|
|
377
|
+
{ prefix: "Float", value: "number" },
|
|
378
|
+
{ prefix: "BigInt", value: "bigint | number" },
|
|
379
|
+
{ prefix: "Decimal", value: "string | number" }
|
|
380
|
+
];
|
|
381
|
+
function updateOpsAlias(params) {
|
|
382
|
+
const { prefix, value, nullable } = params;
|
|
383
|
+
const name = nullable ? `Nullable${prefix}FieldUpdateOperationsInput` : `${prefix}FieldUpdateOperationsInput`;
|
|
384
|
+
return objectAlias({
|
|
385
|
+
name,
|
|
386
|
+
lines: [
|
|
387
|
+
` readonly set?: ${nullable ? `${value} | null` : value};`,
|
|
388
|
+
` readonly increment?: ${value};`,
|
|
389
|
+
` readonly decrement?: ${value};`,
|
|
390
|
+
` readonly multiply?: ${value};`,
|
|
391
|
+
` readonly divide?: ${value};`
|
|
392
|
+
]
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
function filterSection() {
|
|
396
|
+
const parts = [banner({ title: "Scalar filters" })];
|
|
397
|
+
for (const config of SCALAR_FILTER_CONFIGS) {
|
|
398
|
+
parts.push(scalarFilterAlias({ config, nullable: false }));
|
|
399
|
+
parts.push(scalarFilterAlias({ config, nullable: true }));
|
|
400
|
+
parts.push(scalarListFilterAlias({ config }));
|
|
401
|
+
}
|
|
402
|
+
parts.push(objectAlias({
|
|
403
|
+
name: "JsonFilter",
|
|
404
|
+
lines: [` readonly equals?: InputJsonValue;`, ` readonly not?: InputJsonValue;`]
|
|
405
|
+
}));
|
|
406
|
+
parts.push(objectAlias({
|
|
407
|
+
name: "JsonNullableFilter",
|
|
408
|
+
lines: [` readonly equals?: InputJsonValue | null;`, ` readonly not?: InputJsonValue | null;`]
|
|
409
|
+
}));
|
|
410
|
+
parts.push(objectAlias({
|
|
411
|
+
name: "JsonListFilter",
|
|
412
|
+
lines: [
|
|
413
|
+
` readonly equals?: readonly InputJsonValue[] | null;`,
|
|
414
|
+
` readonly has?: InputJsonValue | null;`,
|
|
415
|
+
` readonly hasEvery?: readonly InputJsonValue[];`,
|
|
416
|
+
` readonly hasSome?: readonly InputJsonValue[];`,
|
|
417
|
+
` readonly isEmpty?: boolean;`
|
|
418
|
+
]
|
|
419
|
+
}));
|
|
420
|
+
parts.push(banner({ title: "Atomic update operations" }));
|
|
421
|
+
for (const config of UPDATE_OPS_CONFIGS) {
|
|
422
|
+
parts.push(updateOpsAlias({ ...config, nullable: false }));
|
|
423
|
+
parts.push(updateOpsAlias({ ...config, nullable: true }));
|
|
424
|
+
}
|
|
425
|
+
return parts;
|
|
426
|
+
}
|
|
427
|
+
function emitEnum(params) {
|
|
428
|
+
const { declared } = params;
|
|
429
|
+
const name = declared.name;
|
|
430
|
+
const parts = [banner({ title: `Enum ${name}` })];
|
|
431
|
+
const valueLines = declared.values.map((value) => ` readonly ${propKey({ name: value })}: ${tsString({ value })};`);
|
|
432
|
+
parts.push(`export declare const ${name}: {
|
|
433
|
+
${valueLines.join(`
|
|
434
|
+
`)}
|
|
435
|
+
};`);
|
|
436
|
+
parts.push(`export type ${name} = ${declared.values.map((value) => tsString({ value })).join(" | ")};`);
|
|
437
|
+
const array = `readonly ${name}[]`;
|
|
438
|
+
parts.push(objectAlias({
|
|
439
|
+
name: `${name}Filter`,
|
|
440
|
+
lines: [
|
|
441
|
+
` readonly equals?: ${name};`,
|
|
442
|
+
` readonly in?: ${array};`,
|
|
443
|
+
` readonly notIn?: ${array};`,
|
|
444
|
+
` readonly not?: ${name} | ${name}Filter;`
|
|
445
|
+
]
|
|
446
|
+
}));
|
|
447
|
+
parts.push(objectAlias({
|
|
448
|
+
name: `${name}NullableFilter`,
|
|
449
|
+
lines: [
|
|
450
|
+
` readonly equals?: ${name} | null;`,
|
|
451
|
+
` readonly in?: ${array};`,
|
|
452
|
+
` readonly notIn?: ${array};`,
|
|
453
|
+
` readonly not?: ${name} | ${name}NullableFilter | null;`
|
|
454
|
+
]
|
|
455
|
+
}));
|
|
456
|
+
parts.push(objectAlias({
|
|
457
|
+
name: `${name}ListFilter`,
|
|
458
|
+
lines: [
|
|
459
|
+
` readonly equals?: ${array} | null;`,
|
|
460
|
+
` readonly has?: ${name} | null;`,
|
|
461
|
+
` readonly hasEvery?: ${array};`,
|
|
462
|
+
` readonly hasSome?: ${array};`,
|
|
463
|
+
` readonly isEmpty?: boolean;`
|
|
464
|
+
]
|
|
465
|
+
}));
|
|
466
|
+
return parts.join(`
|
|
467
|
+
|
|
468
|
+
`);
|
|
469
|
+
}
|
|
470
|
+
function createFieldProp(params) {
|
|
471
|
+
const { field } = params;
|
|
472
|
+
const optional = field.isOptional || field.hasDefault || field.isUpdatedAt || field.isList;
|
|
473
|
+
const base = inputBaseType({ field });
|
|
474
|
+
const value = field.isList ? `readonly ${maybeParens({ type: base })}[]` : field.isOptional ? `${base} | null` : base;
|
|
475
|
+
return ` readonly ${propKey({ name: field.name })}${optional ? "?" : ""}: ${value};`;
|
|
476
|
+
}
|
|
477
|
+
function updateFieldProp(params) {
|
|
478
|
+
const { field } = params;
|
|
479
|
+
const base = inputBaseType({ field });
|
|
480
|
+
const key = propKey({ name: field.name });
|
|
481
|
+
if (field.isList) {
|
|
482
|
+
const element = maybeParens({ type: base });
|
|
483
|
+
return ` readonly ${key}?: readonly ${element}[] | { readonly set?: readonly ${element}[]; readonly push?: ${base} | readonly ${element}[] };`;
|
|
484
|
+
}
|
|
485
|
+
const ops = updateOpsName({ field });
|
|
486
|
+
const withNull = field.isOptional ? `${base} | null` : base;
|
|
487
|
+
if (ops !== null)
|
|
488
|
+
return ` readonly ${key}?: ${withNull} | ${ops};`;
|
|
489
|
+
return ` readonly ${key}?: ${withNull} | { readonly set: ${withNull} };`;
|
|
490
|
+
}
|
|
491
|
+
function nestedRefName(params) {
|
|
492
|
+
const { relation, verb } = params;
|
|
493
|
+
const suffix = relation.inverseName === null ? "" : `Without${pascal({ name: relation.inverseName })}`;
|
|
494
|
+
return `${relation.target}${verb}Nested${relation.isList ? "Many" : "One"}${suffix}Input`;
|
|
495
|
+
}
|
|
496
|
+
function checkedCreateLines(params) {
|
|
497
|
+
const { model, omitRelation } = params;
|
|
498
|
+
const lines = [];
|
|
499
|
+
for (const field of model.fields) {
|
|
500
|
+
if (model.fkFieldNames.has(field.name))
|
|
501
|
+
continue;
|
|
502
|
+
lines.push(createFieldProp({ field }));
|
|
503
|
+
}
|
|
504
|
+
for (const relation of model.relations) {
|
|
505
|
+
if (relation.name === omitRelation)
|
|
506
|
+
continue;
|
|
507
|
+
const required = !relation.isList && !relation.isOptional;
|
|
508
|
+
lines.push(` readonly ${propKey({ name: relation.name })}${required ? "" : "?"}: ${nestedRefName({ relation, verb: "Create" })};`);
|
|
509
|
+
}
|
|
510
|
+
return lines;
|
|
511
|
+
}
|
|
512
|
+
function uncheckedCreateLines(params) {
|
|
513
|
+
const { model, omitRelation } = params;
|
|
514
|
+
const omitted = new Set(omitRelation === undefined ? [] : model.relations.find((relation) => relation.name === omitRelation)?.fkFields ?? []);
|
|
515
|
+
return model.fields.filter((field) => !omitted.has(field.name)).map((field) => createFieldProp({ field }));
|
|
516
|
+
}
|
|
517
|
+
function checkedUpdateLines(params) {
|
|
518
|
+
const { model, omitRelation } = params;
|
|
519
|
+
const lines = [];
|
|
520
|
+
for (const field of model.fields) {
|
|
521
|
+
if (model.fkFieldNames.has(field.name))
|
|
522
|
+
continue;
|
|
523
|
+
lines.push(updateFieldProp({ field }));
|
|
524
|
+
}
|
|
525
|
+
for (const relation of model.relations) {
|
|
526
|
+
if (relation.name === omitRelation)
|
|
527
|
+
continue;
|
|
528
|
+
lines.push(` readonly ${propKey({ name: relation.name })}?: ${nestedRefName({ relation, verb: "Update" })};`);
|
|
529
|
+
}
|
|
530
|
+
return lines;
|
|
531
|
+
}
|
|
532
|
+
function uncheckedUpdateLines(params) {
|
|
533
|
+
const { model, omitRelation } = params;
|
|
534
|
+
const omitted = new Set(omitRelation === undefined ? [] : model.relations.find((relation) => relation.name === omitRelation)?.fkFields ?? []);
|
|
535
|
+
return model.fields.filter((field) => !omitted.has(field.name)).map((field) => updateFieldProp({ field }));
|
|
536
|
+
}
|
|
537
|
+
function register(params) {
|
|
538
|
+
const { registry, name, text } = params;
|
|
539
|
+
const existing = registry.get(name);
|
|
540
|
+
if (existing !== undefined) {
|
|
541
|
+
if (existing !== text) {
|
|
542
|
+
throw new VibeError3({
|
|
543
|
+
code: "VIBE_INTERNAL",
|
|
544
|
+
message: `internal: nested input type "${name}" generated with two different bodies`,
|
|
545
|
+
meta: { name }
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
registry.set(name, text);
|
|
551
|
+
}
|
|
552
|
+
function registerNestedContext(params) {
|
|
553
|
+
const { registry, view, relation } = params;
|
|
554
|
+
const target = view.modelByName.get(relation.target);
|
|
555
|
+
if (target === undefined)
|
|
556
|
+
return;
|
|
557
|
+
const t = target.name;
|
|
558
|
+
const inverse = relation.inverseName;
|
|
559
|
+
const suffix = inverse === null ? "" : `Without${pascal({ name: inverse })}`;
|
|
560
|
+
const many = relation.isList;
|
|
561
|
+
const m2m = relation.type === "manyToMany";
|
|
562
|
+
let createUnion;
|
|
563
|
+
let updateUnion;
|
|
564
|
+
if (inverse === null) {
|
|
565
|
+
createUnion = `${t}CreateInput | ${t}UncheckedCreateInput`;
|
|
566
|
+
updateUnion = `${t}UpdateInput | ${t}UncheckedUpdateInput`;
|
|
567
|
+
} else {
|
|
568
|
+
createUnion = `${t}Create${suffix}Input | ${t}UncheckedCreate${suffix}Input`;
|
|
569
|
+
updateUnion = `${t}Update${suffix}Input | ${t}UncheckedUpdate${suffix}Input`;
|
|
570
|
+
register({
|
|
571
|
+
registry,
|
|
572
|
+
name: `${t}Create${suffix}Input`,
|
|
573
|
+
text: objectAlias({
|
|
574
|
+
name: `${t}Create${suffix}Input`,
|
|
575
|
+
lines: checkedCreateLines({ model: target, omitRelation: inverse })
|
|
576
|
+
})
|
|
577
|
+
});
|
|
578
|
+
register({
|
|
579
|
+
registry,
|
|
580
|
+
name: `${t}UncheckedCreate${suffix}Input`,
|
|
581
|
+
text: objectAlias({
|
|
582
|
+
name: `${t}UncheckedCreate${suffix}Input`,
|
|
583
|
+
lines: uncheckedCreateLines({ model: target, omitRelation: inverse })
|
|
584
|
+
})
|
|
585
|
+
});
|
|
586
|
+
register({
|
|
587
|
+
registry,
|
|
588
|
+
name: `${t}Update${suffix}Input`,
|
|
589
|
+
text: objectAlias({
|
|
590
|
+
name: `${t}Update${suffix}Input`,
|
|
591
|
+
lines: checkedUpdateLines({ model: target, omitRelation: inverse })
|
|
592
|
+
})
|
|
593
|
+
});
|
|
594
|
+
register({
|
|
595
|
+
registry,
|
|
596
|
+
name: `${t}UncheckedUpdate${suffix}Input`,
|
|
597
|
+
text: objectAlias({
|
|
598
|
+
name: `${t}UncheckedUpdate${suffix}Input`,
|
|
599
|
+
lines: uncheckedUpdateLines({ model: target, omitRelation: inverse })
|
|
600
|
+
})
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
const connectOrCreateName = `${t}ConnectOrCreate${suffix}Input`;
|
|
604
|
+
register({
|
|
605
|
+
registry,
|
|
606
|
+
name: connectOrCreateName,
|
|
607
|
+
text: objectAlias({
|
|
608
|
+
name: connectOrCreateName,
|
|
609
|
+
lines: [` readonly where: ${t}WhereUniqueInput;`, ` readonly create: ${createUnion};`]
|
|
610
|
+
})
|
|
611
|
+
});
|
|
612
|
+
const createManyData = inverse === null ? `readonly ${t}CreateManyInput[]` : `readonly ${t}UncheckedCreate${suffix}Input[]`;
|
|
613
|
+
if (many) {
|
|
614
|
+
register({
|
|
615
|
+
registry,
|
|
616
|
+
name: `${t}CreateNestedMany${suffix}Input`,
|
|
617
|
+
text: objectAlias({
|
|
618
|
+
name: `${t}CreateNestedMany${suffix}Input`,
|
|
619
|
+
lines: [
|
|
620
|
+
` readonly create?: ${orMany({ type: createUnion })};`,
|
|
621
|
+
` readonly connect?: ${orMany({ type: `${t}WhereUniqueInput` })};`,
|
|
622
|
+
` readonly connectOrCreate?: ${orMany({ type: connectOrCreateName })};`,
|
|
623
|
+
...m2m ? [] : [` readonly createMany?: { readonly data: ${createManyData}; readonly skipDuplicates?: boolean };`]
|
|
624
|
+
]
|
|
625
|
+
})
|
|
626
|
+
});
|
|
627
|
+
const updateWithWhere = `${t}UpdateWithWhereUnique${suffix}Input`;
|
|
628
|
+
register({
|
|
629
|
+
registry,
|
|
630
|
+
name: updateWithWhere,
|
|
631
|
+
text: objectAlias({
|
|
632
|
+
name: updateWithWhere,
|
|
633
|
+
lines: [` readonly where: ${t}WhereUniqueInput;`, ` readonly data: ${updateUnion};`]
|
|
634
|
+
})
|
|
635
|
+
});
|
|
636
|
+
const updateManyWithWhere = `${t}UpdateManyWithWhere${suffix}Input`;
|
|
637
|
+
register({
|
|
638
|
+
registry,
|
|
639
|
+
name: updateManyWithWhere,
|
|
640
|
+
text: objectAlias({
|
|
641
|
+
name: updateManyWithWhere,
|
|
642
|
+
lines: [` readonly where: ${t}WhereInput;`, ` readonly data: ${t}UpdateManyInput;`]
|
|
643
|
+
})
|
|
644
|
+
});
|
|
645
|
+
const upsertWithWhere = `${t}UpsertWithWhereUnique${suffix}Input`;
|
|
646
|
+
register({
|
|
647
|
+
registry,
|
|
648
|
+
name: upsertWithWhere,
|
|
649
|
+
text: objectAlias({
|
|
650
|
+
name: upsertWithWhere,
|
|
651
|
+
lines: [
|
|
652
|
+
` readonly where: ${t}WhereUniqueInput;`,
|
|
653
|
+
` readonly create: ${createUnion};`,
|
|
654
|
+
` readonly update: ${updateUnion};`
|
|
655
|
+
]
|
|
656
|
+
})
|
|
657
|
+
});
|
|
658
|
+
register({
|
|
659
|
+
registry,
|
|
660
|
+
name: `${t}UpdateNestedMany${suffix}Input`,
|
|
661
|
+
text: objectAlias({
|
|
662
|
+
name: `${t}UpdateNestedMany${suffix}Input`,
|
|
663
|
+
lines: [
|
|
664
|
+
` readonly create?: ${orMany({ type: createUnion })};`,
|
|
665
|
+
` readonly connect?: ${orMany({ type: `${t}WhereUniqueInput` })};`,
|
|
666
|
+
` readonly connectOrCreate?: ${orMany({ type: connectOrCreateName })};`,
|
|
667
|
+
...m2m ? [] : [` readonly createMany?: { readonly data: ${createManyData}; readonly skipDuplicates?: boolean };`],
|
|
668
|
+
` readonly set?: ${orMany({ type: `${t}WhereUniqueInput` })};`,
|
|
669
|
+
` readonly disconnect?: ${orMany({ type: `${t}WhereUniqueInput` })};`,
|
|
670
|
+
` readonly delete?: ${orMany({ type: `${t}WhereUniqueInput` })};`,
|
|
671
|
+
` readonly update?: ${orMany({ type: updateWithWhere })};`,
|
|
672
|
+
` readonly updateMany?: ${orMany({ type: updateManyWithWhere })};`,
|
|
673
|
+
` readonly deleteMany?: ${orMany({ type: `${t}WhereInput` })};`,
|
|
674
|
+
` readonly upsert?: ${orMany({ type: upsertWithWhere })};`
|
|
675
|
+
]
|
|
676
|
+
})
|
|
677
|
+
});
|
|
678
|
+
} else {
|
|
679
|
+
const allowDetach = inverse === null || relation.isOptional;
|
|
680
|
+
register({
|
|
681
|
+
registry,
|
|
682
|
+
name: `${t}CreateNestedOne${suffix}Input`,
|
|
683
|
+
text: objectAlias({
|
|
684
|
+
name: `${t}CreateNestedOne${suffix}Input`,
|
|
685
|
+
lines: [
|
|
686
|
+
` readonly create?: ${createUnion};`,
|
|
687
|
+
` readonly connect?: ${t}WhereUniqueInput;`,
|
|
688
|
+
` readonly connectOrCreate?: ${connectOrCreateName};`
|
|
689
|
+
]
|
|
690
|
+
})
|
|
691
|
+
});
|
|
692
|
+
register({
|
|
693
|
+
registry,
|
|
694
|
+
name: `${t}UpdateNestedOne${suffix}Input`,
|
|
695
|
+
text: objectAlias({
|
|
696
|
+
name: `${t}UpdateNestedOne${suffix}Input`,
|
|
697
|
+
lines: [
|
|
698
|
+
` readonly create?: ${createUnion};`,
|
|
699
|
+
` readonly connect?: ${t}WhereUniqueInput;`,
|
|
700
|
+
` readonly connectOrCreate?: ${connectOrCreateName};`,
|
|
701
|
+
` readonly update?: ${updateUnion};`,
|
|
702
|
+
` readonly upsert?: { readonly create: ${createUnion}; readonly update: ${updateUnion} };`,
|
|
703
|
+
...allowDetach ? [` readonly disconnect?: boolean;`, ` readonly delete?: boolean;`] : []
|
|
704
|
+
]
|
|
705
|
+
})
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
var AGGREGATE_OPS_UNION = `"_count" | "_avg" | "_sum" | "_min" | "_max"`;
|
|
710
|
+
function emitModel(params) {
|
|
711
|
+
const { model, view, registry } = params;
|
|
712
|
+
const computedEntries = params.computed ?? [];
|
|
713
|
+
const n = model.name;
|
|
714
|
+
const hasRelations = model.relations.length > 0;
|
|
715
|
+
const parts = [banner({ title: `Model ${n}` })];
|
|
716
|
+
for (const relation of model.relations) {
|
|
717
|
+
registerNestedContext({ registry, view, relation });
|
|
718
|
+
}
|
|
719
|
+
parts.push(`export type ${n} = {
|
|
720
|
+
${[
|
|
721
|
+
...model.fields.map((field) => ` ${propKey({ name: field.name })}: ${payloadFieldType({ field })};`),
|
|
722
|
+
...computedEntries.map((entry) => ` ${propKey({ name: entry.field })}: ${entry.type};`)
|
|
723
|
+
].join(`
|
|
724
|
+
`)}
|
|
725
|
+
};`);
|
|
726
|
+
parts.push(`export type ${n}ScalarFieldEnum = ${model.fields.map((field) => tsString({ value: field.name })).join(" | ")};`);
|
|
727
|
+
if (computedEntries.length > 0) {
|
|
728
|
+
parts.push(`export type ${n}SelectableFieldEnum = ${n}ScalarFieldEnum | ${computedEntries.map((entry) => tsString({ value: entry.field })).join(" | ")};`);
|
|
729
|
+
}
|
|
730
|
+
const selectableEnum = computedEntries.length > 0 ? `${n}SelectableFieldEnum` : `${n}ScalarFieldEnum`;
|
|
731
|
+
if (hasRelations) {
|
|
732
|
+
parts.push(`export type ${n}Relations = {
|
|
733
|
+
${model.relations.map((relation) => ` ${propKey({ name: relation.name })}: ${relationPayloadType({ relation })};`).join(`
|
|
734
|
+
`)}
|
|
735
|
+
};`);
|
|
736
|
+
}
|
|
737
|
+
const listRelations = model.relations.filter((relation) => relation.isList);
|
|
738
|
+
const hasCounts = listRelations.length > 0;
|
|
739
|
+
const countLine = ` readonly _count?: boolean | { readonly select: ${n}CountSelect };`;
|
|
740
|
+
const relationSelectValue = (relation) => `boolean | ${relation.target}${relation.isList ? "FindManyArgs" : "Args"}`;
|
|
741
|
+
if (hasCounts) {
|
|
742
|
+
parts.push(objectAlias({
|
|
743
|
+
name: `${n}CountSelect`,
|
|
744
|
+
lines: listRelations.map((relation) => ` readonly ${propKey({ name: relation.name })}?: boolean;`)
|
|
745
|
+
}));
|
|
746
|
+
}
|
|
747
|
+
parts.push(objectAlias({
|
|
748
|
+
name: `${n}Select`,
|
|
749
|
+
lines: [
|
|
750
|
+
...model.fields.map((field) => ` readonly ${propKey({ name: field.name })}?: boolean;`),
|
|
751
|
+
...computedEntries.map((entry) => ` readonly ${propKey({ name: entry.field })}?: boolean;`),
|
|
752
|
+
...model.relations.map((relation) => ` readonly ${propKey({ name: relation.name })}?: ${relationSelectValue(relation)};`),
|
|
753
|
+
...hasCounts ? [countLine] : []
|
|
754
|
+
]
|
|
755
|
+
}));
|
|
756
|
+
parts.push(objectAlias({
|
|
757
|
+
name: `${n}Include`,
|
|
758
|
+
lines: [
|
|
759
|
+
...model.relations.map((relation) => ` readonly ${propKey({ name: relation.name })}?: ${relationSelectValue(relation)};`),
|
|
760
|
+
...hasCounts ? [countLine] : []
|
|
761
|
+
]
|
|
762
|
+
}));
|
|
763
|
+
parts.push(objectAlias({
|
|
764
|
+
name: `${n}OmitInput`,
|
|
765
|
+
lines: [
|
|
766
|
+
...model.fields.map((field) => ` readonly ${propKey({ name: field.name })}?: boolean;`),
|
|
767
|
+
...computedEntries.map((entry) => ` readonly ${propKey({ name: entry.field })}?: boolean;`)
|
|
768
|
+
]
|
|
769
|
+
}));
|
|
770
|
+
parts.push(objectAlias({
|
|
771
|
+
name: `${n}Args`,
|
|
772
|
+
lines: [
|
|
773
|
+
` readonly select?: ${n}Select;`,
|
|
774
|
+
` readonly include?: ${n}Include;`,
|
|
775
|
+
` readonly omit?: ${n}OmitInput;`,
|
|
776
|
+
` readonly relationStrategy?: RelationStrategy;`
|
|
777
|
+
]
|
|
778
|
+
}));
|
|
779
|
+
parts.push(objectAlias({
|
|
780
|
+
name: `${n}ListRelationFilter`,
|
|
781
|
+
lines: [
|
|
782
|
+
` readonly some?: ${n}WhereInput;`,
|
|
783
|
+
` readonly every?: ${n}WhereInput;`,
|
|
784
|
+
` readonly none?: ${n}WhereInput;`
|
|
785
|
+
]
|
|
786
|
+
}));
|
|
787
|
+
parts.push(objectAlias({
|
|
788
|
+
name: `${n}RelationFilter`,
|
|
789
|
+
lines: [` readonly is?: ${n}WhereInput | null;`, ` readonly isNot?: ${n}WhereInput | null;`]
|
|
790
|
+
}));
|
|
791
|
+
const whereLines = [
|
|
792
|
+
` readonly AND?: ${orMany({ type: `${n}WhereInput` })};`,
|
|
793
|
+
` readonly OR?: readonly ${n}WhereInput[];`,
|
|
794
|
+
` readonly NOT?: ${orMany({ type: `${n}WhereInput` })};`
|
|
795
|
+
];
|
|
796
|
+
for (const field of model.fields) {
|
|
797
|
+
const key = propKey({ name: field.name });
|
|
798
|
+
if (field.isList || field.scalar === "Json") {
|
|
799
|
+
whereLines.push(` readonly ${key}?: ${filterName({ field })};`);
|
|
800
|
+
continue;
|
|
801
|
+
}
|
|
802
|
+
const nullable = field.isOptional ? " | null" : "";
|
|
803
|
+
whereLines.push(` readonly ${key}?: ${inputBaseType({ field })} | ${filterName({ field })}${nullable};`);
|
|
804
|
+
}
|
|
805
|
+
for (const relation of model.relations) {
|
|
806
|
+
const key = propKey({ name: relation.name });
|
|
807
|
+
if (relation.isList) {
|
|
808
|
+
whereLines.push(` readonly ${key}?: ${relation.target}ListRelationFilter;`);
|
|
809
|
+
} else {
|
|
810
|
+
whereLines.push(` readonly ${key}?: ${relation.target}RelationFilter | ${relation.target}WhereInput${relation.isOptional ? " | null" : ""};`);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
parts.push(objectAlias({ name: `${n}WhereInput`, lines: whereLines }));
|
|
814
|
+
if (model.uniques.length === 0) {
|
|
815
|
+
parts.push(`export type ${n}WhereUniqueInput = never;`);
|
|
816
|
+
} else {
|
|
817
|
+
const members = model.uniques.map((member) => {
|
|
818
|
+
const first = member.fields[0];
|
|
819
|
+
if (member.fields.length === 1 && first !== undefined) {
|
|
820
|
+
return `{ readonly ${propKey({ name: first.name })}: ${inputBaseType({ field: first })} }`;
|
|
821
|
+
}
|
|
822
|
+
const inner = member.fields.map((field) => `readonly ${propKey({ name: field.name })}: ${inputBaseType({ field })}`).join("; ");
|
|
823
|
+
return `{ readonly ${propKey({ name: member.key })}: { ${inner} } }`;
|
|
824
|
+
});
|
|
825
|
+
parts.push(`export type ${n}WhereUniqueInput =
|
|
826
|
+
| ${members.join(`
|
|
827
|
+
| `)};`);
|
|
828
|
+
}
|
|
829
|
+
parts.push(objectAlias({
|
|
830
|
+
name: `${n}OrderByInput`,
|
|
831
|
+
lines: model.fields.map((field) => ` readonly ${propKey({ name: field.name })}?: ${field.isOptional ? "SortOrderWithNulls" : "SortOrder"};`)
|
|
832
|
+
}));
|
|
833
|
+
parts.push(objectAlias({ name: `${n}CreateInput`, lines: checkedCreateLines({ model }) }));
|
|
834
|
+
parts.push(objectAlias({ name: `${n}UncheckedCreateInput`, lines: uncheckedCreateLines({ model }) }));
|
|
835
|
+
parts.push(`export type ${n}CreateManyInput = ${n}UncheckedCreateInput;`);
|
|
836
|
+
parts.push(objectAlias({ name: `${n}UpdateInput`, lines: checkedUpdateLines({ model }) }));
|
|
837
|
+
parts.push(objectAlias({ name: `${n}UncheckedUpdateInput`, lines: uncheckedUpdateLines({ model }) }));
|
|
838
|
+
parts.push(`export type ${n}UpdateManyInput = ${n}UncheckedUpdateInput;`);
|
|
839
|
+
const numericFields = model.fields.filter((field) => !field.isList && field.scalar !== null && NUMERIC_SCALARS.has(field.scalar));
|
|
840
|
+
const minmaxFields = model.fields.filter((field) => !field.isList && field.scalar !== null && MINMAX_SCALARS.has(field.scalar));
|
|
841
|
+
parts.push(objectAlias({
|
|
842
|
+
name: `${n}CountAggregateInput`,
|
|
843
|
+
lines: [
|
|
844
|
+
...model.fields.map((field) => ` readonly ${propKey({ name: field.name })}?: true;`),
|
|
845
|
+
` readonly _all?: true;`
|
|
846
|
+
]
|
|
847
|
+
}));
|
|
848
|
+
const aggregateOpProps = [` readonly _count?: true | ${n}CountAggregateInput;`];
|
|
849
|
+
if (numericFields.length > 0) {
|
|
850
|
+
parts.push(objectAlias({
|
|
851
|
+
name: `${n}AvgAggregateInput`,
|
|
852
|
+
lines: numericFields.map((field) => ` readonly ${propKey({ name: field.name })}?: true;`)
|
|
853
|
+
}));
|
|
854
|
+
parts.push(`export type ${n}SumAggregateInput = ${n}AvgAggregateInput;`);
|
|
855
|
+
aggregateOpProps.push(` readonly _avg?: ${n}AvgAggregateInput;`);
|
|
856
|
+
aggregateOpProps.push(` readonly _sum?: ${n}SumAggregateInput;`);
|
|
857
|
+
}
|
|
858
|
+
if (minmaxFields.length > 0) {
|
|
859
|
+
parts.push(objectAlias({
|
|
860
|
+
name: `${n}MinAggregateInput`,
|
|
861
|
+
lines: minmaxFields.map((field) => ` readonly ${propKey({ name: field.name })}?: true;`)
|
|
862
|
+
}));
|
|
863
|
+
parts.push(`export type ${n}MaxAggregateInput = ${n}MinAggregateInput;`);
|
|
864
|
+
aggregateOpProps.push(` readonly _min?: ${n}MinAggregateInput;`);
|
|
865
|
+
aggregateOpProps.push(` readonly _max?: ${n}MaxAggregateInput;`);
|
|
866
|
+
}
|
|
867
|
+
parts.push(objectAlias({
|
|
868
|
+
name: `${n}FindManyArgs`,
|
|
869
|
+
lines: [
|
|
870
|
+
` readonly where?: ${n}WhereInput;`,
|
|
871
|
+
` readonly orderBy?: ${orMany({ type: `${n}OrderByInput` })};`,
|
|
872
|
+
` readonly select?: ${n}Select;`,
|
|
873
|
+
` readonly include?: ${n}Include;`,
|
|
874
|
+
` readonly omit?: ${n}OmitInput;`,
|
|
875
|
+
` readonly cursor?: ${n}WhereUniqueInput;`,
|
|
876
|
+
` readonly take?: number;`,
|
|
877
|
+
` readonly skip?: number;`,
|
|
878
|
+
` readonly distinct?: ${orMany({ type: `${n}ScalarFieldEnum` })};`,
|
|
879
|
+
` readonly relationStrategy?: RelationStrategy;`
|
|
880
|
+
]
|
|
881
|
+
}));
|
|
882
|
+
parts.push(`export type ${n}FindFirstArgs = ${n}FindManyArgs;`);
|
|
883
|
+
parts.push(objectAlias({
|
|
884
|
+
name: `${n}FindUniqueArgs`,
|
|
885
|
+
lines: [
|
|
886
|
+
` readonly where: ${n}WhereUniqueInput;`,
|
|
887
|
+
` readonly select?: ${n}Select;`,
|
|
888
|
+
` readonly include?: ${n}Include;`,
|
|
889
|
+
` readonly omit?: ${n}OmitInput;`,
|
|
890
|
+
` readonly relationStrategy?: RelationStrategy;`
|
|
891
|
+
]
|
|
892
|
+
}));
|
|
893
|
+
parts.push(objectAlias({
|
|
894
|
+
name: `${n}CreateArgs`,
|
|
895
|
+
lines: [
|
|
896
|
+
` readonly data: ${n}CreateInput | ${n}UncheckedCreateInput;`,
|
|
897
|
+
` readonly select?: ${n}Select;`,
|
|
898
|
+
` readonly include?: ${n}Include;`,
|
|
899
|
+
` readonly omit?: ${n}OmitInput;`
|
|
900
|
+
]
|
|
901
|
+
}));
|
|
902
|
+
parts.push(objectAlias({
|
|
903
|
+
name: `${n}CreateManyArgs`,
|
|
904
|
+
lines: [
|
|
905
|
+
` readonly data: ${orMany({ type: `${n}CreateManyInput` })};`,
|
|
906
|
+
` readonly skipDuplicates?: boolean;`
|
|
907
|
+
]
|
|
908
|
+
}));
|
|
909
|
+
parts.push(objectAlias({
|
|
910
|
+
name: `${n}CreateManyAndReturnArgs`,
|
|
911
|
+
lines: [
|
|
912
|
+
` readonly data: ${orMany({ type: `${n}CreateManyInput` })};`,
|
|
913
|
+
` readonly skipDuplicates?: boolean;`,
|
|
914
|
+
` readonly select?: ${n}Select;`,
|
|
915
|
+
` readonly omit?: ${n}OmitInput;`
|
|
916
|
+
]
|
|
917
|
+
}));
|
|
918
|
+
parts.push(objectAlias({
|
|
919
|
+
name: `${n}UpdateArgs`,
|
|
920
|
+
lines: [
|
|
921
|
+
` readonly where: ${n}WhereUniqueInput;`,
|
|
922
|
+
` readonly data: ${n}UpdateInput | ${n}UncheckedUpdateInput;`,
|
|
923
|
+
` readonly select?: ${n}Select;`,
|
|
924
|
+
` readonly include?: ${n}Include;`,
|
|
925
|
+
` readonly omit?: ${n}OmitInput;`
|
|
926
|
+
]
|
|
927
|
+
}));
|
|
928
|
+
parts.push(objectAlias({
|
|
929
|
+
name: `${n}UpdateManyArgs`,
|
|
930
|
+
lines: [` readonly where?: ${n}WhereInput;`, ` readonly data: ${n}UpdateManyInput;`]
|
|
931
|
+
}));
|
|
932
|
+
parts.push(objectAlias({
|
|
933
|
+
name: `${n}UpsertArgs`,
|
|
934
|
+
lines: [
|
|
935
|
+
` readonly where: ${n}WhereUniqueInput;`,
|
|
936
|
+
` readonly create: ${n}CreateInput | ${n}UncheckedCreateInput;`,
|
|
937
|
+
` readonly update: ${n}UpdateInput | ${n}UncheckedUpdateInput;`,
|
|
938
|
+
` readonly select?: ${n}Select;`,
|
|
939
|
+
` readonly include?: ${n}Include;`,
|
|
940
|
+
` readonly omit?: ${n}OmitInput;`
|
|
941
|
+
]
|
|
942
|
+
}));
|
|
943
|
+
parts.push(objectAlias({
|
|
944
|
+
name: `${n}DeleteArgs`,
|
|
945
|
+
lines: [
|
|
946
|
+
` readonly where: ${n}WhereUniqueInput;`,
|
|
947
|
+
` readonly select?: ${n}Select;`,
|
|
948
|
+
` readonly include?: ${n}Include;`,
|
|
949
|
+
` readonly omit?: ${n}OmitInput;`
|
|
950
|
+
]
|
|
951
|
+
}));
|
|
952
|
+
parts.push(objectAlias({ name: `${n}DeleteManyArgs`, lines: [` readonly where?: ${n}WhereInput;`] }));
|
|
953
|
+
parts.push(objectAlias({
|
|
954
|
+
name: `${n}CountArgs`,
|
|
955
|
+
lines: [
|
|
956
|
+
` readonly where?: ${n}WhereInput;`,
|
|
957
|
+
` readonly orderBy?: ${orMany({ type: `${n}OrderByInput` })};`,
|
|
958
|
+
` readonly cursor?: ${n}WhereUniqueInput;`,
|
|
959
|
+
` readonly take?: number;`,
|
|
960
|
+
` readonly skip?: number;`
|
|
961
|
+
]
|
|
962
|
+
}));
|
|
963
|
+
parts.push(objectAlias({
|
|
964
|
+
name: `${n}AggregateArgs`,
|
|
965
|
+
lines: [
|
|
966
|
+
` readonly where?: ${n}WhereInput;`,
|
|
967
|
+
` readonly orderBy?: ${orMany({ type: `${n}OrderByInput` })};`,
|
|
968
|
+
` readonly cursor?: ${n}WhereUniqueInput;`,
|
|
969
|
+
` readonly take?: number;`,
|
|
970
|
+
` readonly skip?: number;`,
|
|
971
|
+
...aggregateOpProps
|
|
972
|
+
]
|
|
973
|
+
}));
|
|
974
|
+
parts.push(objectAlias({
|
|
975
|
+
name: `${n}GroupByArgs`,
|
|
976
|
+
lines: [
|
|
977
|
+
` readonly by: readonly ${n}ScalarFieldEnum[];`,
|
|
978
|
+
` readonly where?: ${n}WhereInput;`,
|
|
979
|
+
` readonly orderBy?: ${orMany({ type: `${n}OrderByInput` })};`,
|
|
980
|
+
` readonly having?: ${n}WhereInput;`,
|
|
981
|
+
` readonly take?: number;`,
|
|
982
|
+
` readonly skip?: number;`,
|
|
983
|
+
...aggregateOpProps
|
|
984
|
+
]
|
|
985
|
+
}));
|
|
986
|
+
const falseFilter = (source) => `${source}[K] extends false | undefined ? never : K`;
|
|
987
|
+
parts.push(`type ${n}OmitBase<A> = A extends { readonly omit: infer O extends object } ? Omit<${n}, OmittedKeys<O>> : ${n};`);
|
|
988
|
+
if (hasCounts) {
|
|
989
|
+
const listUnion = listRelations.map((relation) => tsString({ value: relation.name })).join(" | ");
|
|
990
|
+
const allCounts = listRelations.map((relation) => `${propKey({ name: relation.name })}: number`).join("; ");
|
|
991
|
+
parts.push(`type ${n}CountResult<C> = C extends { readonly select: infer CS extends object }
|
|
992
|
+
` + ` ? { [K in keyof CS & (${listUnion}) as ${falseFilter("CS")}]: number }
|
|
993
|
+
` + ` : { ${allCounts} };`);
|
|
994
|
+
parts.push(`type ${n}CountPayload<S> = S extends { readonly _count: infer C extends true | object } ? { _count: ${n}CountResult<C> } : unknown;`);
|
|
995
|
+
}
|
|
996
|
+
if (hasRelations) {
|
|
997
|
+
const relationCases = model.relations.map((relation) => {
|
|
998
|
+
const nested = `${relation.target}GetPayload<A>`;
|
|
999
|
+
const guard = `A extends { readonly select: unknown } | { readonly include: unknown } | { readonly omit: unknown }`;
|
|
1000
|
+
let expression;
|
|
1001
|
+
if (relation.isList) {
|
|
1002
|
+
expression = `${guard} ? ${nested}[] : ${relation.target}[]`;
|
|
1003
|
+
} else if (relation.isOptional) {
|
|
1004
|
+
expression = `(${guard} ? ${nested} : ${relation.target}) | null`;
|
|
1005
|
+
} else {
|
|
1006
|
+
expression = `${guard} ? ${nested} : ${relation.target}`;
|
|
1007
|
+
}
|
|
1008
|
+
return ` ${propKey({ name: relation.name })}: ${expression};`;
|
|
1009
|
+
}).join(`
|
|
1010
|
+
`);
|
|
1011
|
+
parts.push(`type ${n}RelationPayload<K extends keyof ${n}Relations, A> = {
|
|
1012
|
+
${relationCases}
|
|
1013
|
+
}[K];`);
|
|
1014
|
+
parts.push(`type ${n}IncludePayload<A> = A extends { readonly include: infer I extends object }
|
|
1015
|
+
` + ` ? { [K in keyof I & keyof ${n}Relations as ${falseFilter("I")}]: ${n}RelationPayload<K, I[K]> }${hasCounts ? ` & ${n}CountPayload<I>` : ""}
|
|
1016
|
+
` + ` : unknown;`);
|
|
1017
|
+
parts.push(`export type ${n}GetPayload<A> = A extends { readonly select: infer S extends object }
|
|
1018
|
+
` + ` ? { [K in keyof S & ${selectableEnum} as ${falseFilter("S")}]: ${n}[K & keyof ${n}] } &
|
|
1019
|
+
` + ` { [K in keyof S & keyof ${n}Relations as ${falseFilter("S")}]: ${n}RelationPayload<K, S[K]> }${hasCounts ? ` &
|
|
1020
|
+
${n}CountPayload<S>` : ""}
|
|
1021
|
+
` + ` : ${n}OmitBase<A> & ${n}IncludePayload<A>;`);
|
|
1022
|
+
} else {
|
|
1023
|
+
parts.push(`export type ${n}GetPayload<A> = A extends { readonly select: infer S extends object }
|
|
1024
|
+
` + ` ? { [K in keyof S & ${selectableEnum} as ${falseFilter("S")}]: ${n}[K & keyof ${n}] }
|
|
1025
|
+
` + ` : ${n}OmitBase<A>;`);
|
|
1026
|
+
}
|
|
1027
|
+
parts.push(`export type ${n}AggregateResult<A> = {
|
|
1028
|
+
` + ` [K in keyof A & (${AGGREGATE_OPS_UNION})]:
|
|
1029
|
+
` + ` K extends "_count" ? (A[K] extends true ? number : { [F in keyof A[K]]: number })
|
|
1030
|
+
` + ` : K extends "_avg" ? { [F in keyof A[K]]: number | null }
|
|
1031
|
+
` + ` : { [F in keyof A[K] & keyof ${n}]: ${n}[F & keyof ${n}] | null };
|
|
1032
|
+
` + `};`);
|
|
1033
|
+
parts.push(`export type ${n}GroupByResult<A extends ${n}GroupByArgs> =
|
|
1034
|
+
` + ` { [K in A["by"][number]]: ${n}[K & keyof ${n}] } & ${n}AggregateResult<A>;`);
|
|
1035
|
+
const readMethods = [
|
|
1036
|
+
` findMany<A extends ${n}FindManyArgs = ${n}FindManyArgs>(args?: A): Promise<${n}GetPayload<A>[]>;`,
|
|
1037
|
+
` findFirst<A extends ${n}FindFirstArgs = ${n}FindFirstArgs>(args?: A): Promise<${n}GetPayload<A> | null>;`,
|
|
1038
|
+
` findFirstOrThrow<A extends ${n}FindFirstArgs = ${n}FindFirstArgs>(args?: A): Promise<${n}GetPayload<A>>;`,
|
|
1039
|
+
` findUnique<A extends ${n}FindUniqueArgs>(args: A): Promise<${n}GetPayload<A> | null>;`,
|
|
1040
|
+
` findUniqueOrThrow<A extends ${n}FindUniqueArgs>(args: A): Promise<${n}GetPayload<A>>;`
|
|
1041
|
+
];
|
|
1042
|
+
const writeMethods = [
|
|
1043
|
+
` create<A extends ${n}CreateArgs>(args: A): Promise<${n}GetPayload<A>>;`,
|
|
1044
|
+
` createMany(args: ${n}CreateManyArgs): Promise<BatchPayload>;`,
|
|
1045
|
+
` createManyAndReturn<A extends ${n}CreateManyAndReturnArgs>(args: A): Promise<${n}GetPayload<A>[]>;`,
|
|
1046
|
+
` update<A extends ${n}UpdateArgs>(args: A): Promise<${n}GetPayload<A>>;`,
|
|
1047
|
+
` updateMany(args: ${n}UpdateManyArgs): Promise<BatchPayload>;`,
|
|
1048
|
+
` upsert<A extends ${n}UpsertArgs>(args: A): Promise<${n}GetPayload<A>>;`,
|
|
1049
|
+
` delete<A extends ${n}DeleteArgs>(args: A): Promise<${n}GetPayload<A>>;`,
|
|
1050
|
+
` deleteMany(args?: ${n}DeleteManyArgs): Promise<BatchPayload>;`
|
|
1051
|
+
];
|
|
1052
|
+
const aggregateMethods = [
|
|
1053
|
+
` count(args?: ${n}CountArgs): Promise<number>;`,
|
|
1054
|
+
` aggregate<A extends ${n}AggregateArgs>(args: A): Promise<${n}AggregateResult<A>>;`,
|
|
1055
|
+
` groupBy<A extends ${n}GroupByArgs>(args: A): Promise<${n}GroupByResult<A>[]>;`
|
|
1056
|
+
];
|
|
1057
|
+
const delegateMethods = model.isView ? [...readMethods, ...aggregateMethods] : [...readMethods, ...writeMethods, ...aggregateMethods];
|
|
1058
|
+
delegateMethods.push(...params.extensionLines ?? []);
|
|
1059
|
+
parts.push(`export type ${n}Delegate = {
|
|
1060
|
+
${delegateMethods.join(`
|
|
1061
|
+
`)}
|
|
1062
|
+
};`);
|
|
1063
|
+
return parts.join(`
|
|
1064
|
+
|
|
1065
|
+
`);
|
|
1066
|
+
}
|
|
1067
|
+
function emitViewTypesForModel(params) {
|
|
1068
|
+
const { model, view } = params;
|
|
1069
|
+
const n = model.name;
|
|
1070
|
+
const hasRelations = model.relations.length > 0;
|
|
1071
|
+
const parts = [];
|
|
1072
|
+
parts.push(objectAlias({
|
|
1073
|
+
name: `${n}ViewFields`,
|
|
1074
|
+
lines: model.fields.map((field) => ` readonly ${propKey({ name: field.name })}?: true;`)
|
|
1075
|
+
}));
|
|
1076
|
+
parts.push(objectAlias({
|
|
1077
|
+
name: `${n}ViewSelect`,
|
|
1078
|
+
lines: model.fields.map((field) => ` readonly ${propKey({ name: field.name })}?: boolean;`)
|
|
1079
|
+
}));
|
|
1080
|
+
parts.push(`type ${n}ViewScalars<F> = { [K in keyof F & ${n}ScalarFieldEnum]: ${n}[K & keyof ${n}] };`);
|
|
1081
|
+
if (hasRelations) {
|
|
1082
|
+
parts.push(objectAlias({
|
|
1083
|
+
name: `${n}ViewInclude`,
|
|
1084
|
+
lines: model.relations.map((relation) => ` readonly ${propKey({ name: relation.name })}?: boolean | ${relation.target}ViewNestedArgs;`)
|
|
1085
|
+
}));
|
|
1086
|
+
}
|
|
1087
|
+
parts.push(objectAlias({
|
|
1088
|
+
name: `${n}ViewNestedArgs`,
|
|
1089
|
+
lines: [
|
|
1090
|
+
` readonly where?: ${n}WhereInput;`,
|
|
1091
|
+
` readonly orderBy?: ${orMany({ type: `${n}OrderByInput` })};`,
|
|
1092
|
+
` readonly take?: number;`,
|
|
1093
|
+
` readonly skip?: number;`,
|
|
1094
|
+
` readonly select?: ${n}ViewSelect;`,
|
|
1095
|
+
` readonly reveal?: ${n}ViewSelect;`,
|
|
1096
|
+
...hasRelations ? [` readonly include?: ${n}ViewInclude;`] : []
|
|
1097
|
+
]
|
|
1098
|
+
}));
|
|
1099
|
+
parts.push(objectAlias({
|
|
1100
|
+
name: `${n}ViewFindManyArgs`,
|
|
1101
|
+
lines: [
|
|
1102
|
+
` readonly where?: ${n}WhereInput;`,
|
|
1103
|
+
` readonly orderBy?: ${orMany({ type: `${n}OrderByInput` })};`,
|
|
1104
|
+
` readonly select?: ${n}ViewSelect;`,
|
|
1105
|
+
` readonly reveal?: ${n}ViewSelect;`,
|
|
1106
|
+
...hasRelations ? [` readonly include?: ${n}ViewInclude;`] : [],
|
|
1107
|
+
` readonly cursor?: ${n}WhereUniqueInput;`,
|
|
1108
|
+
` readonly take?: number;`,
|
|
1109
|
+
` readonly skip?: number;`,
|
|
1110
|
+
` readonly distinct?: ${orMany({ type: `${n}ScalarFieldEnum` })};`,
|
|
1111
|
+
` readonly relationStrategy?: RelationStrategy;`
|
|
1112
|
+
]
|
|
1113
|
+
}));
|
|
1114
|
+
parts.push(objectAlias({
|
|
1115
|
+
name: `${n}ViewFindUniqueArgs`,
|
|
1116
|
+
lines: [
|
|
1117
|
+
` readonly where: ${n}WhereUniqueInput;`,
|
|
1118
|
+
` readonly select?: ${n}ViewSelect;`,
|
|
1119
|
+
` readonly reveal?: ${n}ViewSelect;`,
|
|
1120
|
+
...hasRelations ? [` readonly include?: ${n}ViewInclude;`] : [],
|
|
1121
|
+
` readonly relationStrategy?: RelationStrategy;`
|
|
1122
|
+
]
|
|
1123
|
+
}));
|
|
1124
|
+
const falseFilter = (source) => `${source}[K] extends false | undefined ? never : K`;
|
|
1125
|
+
parts.push(`type ${n}ViewSelectScalars<F, A> = A extends { readonly select: infer S extends object }
|
|
1126
|
+
` + ` ? { [K in keyof S & keyof F & ${n}ScalarFieldEnum as ${falseFilter("S")}]: ${n}[K & keyof ${n}] }
|
|
1127
|
+
` + ` : ${n}ViewScalars<F>;`);
|
|
1128
|
+
parts.push(`type ${n}ViewReveal<A> = A extends { readonly reveal: infer R extends object }
|
|
1129
|
+
` + ` ? { [K in keyof R & ${n}ScalarFieldEnum as ${falseFilter("R")}]: ${n}[K & keyof ${n}] }
|
|
1130
|
+
` + ` : unknown;`);
|
|
1131
|
+
if (hasRelations) {
|
|
1132
|
+
const relationCases = model.relations.map((relation) => {
|
|
1133
|
+
const target = relation.target;
|
|
1134
|
+
const targetClientKey = view.modelByName.get(target)?.clientName ?? target;
|
|
1135
|
+
const guard = `D extends { readonly ${propKey({ name: targetClientKey })}: infer RF extends object }`;
|
|
1136
|
+
const masked = `ViewRow<${target}ViewScalars<RF>>`;
|
|
1137
|
+
let expression;
|
|
1138
|
+
if (relation.isList) {
|
|
1139
|
+
expression = `${guard} ? ${masked}[] : ${target}[]`;
|
|
1140
|
+
} else if (relation.isOptional) {
|
|
1141
|
+
expression = `(${guard} ? ${masked} : ${target}) | null`;
|
|
1142
|
+
} else {
|
|
1143
|
+
expression = `${guard} ? ${masked} : ${target}`;
|
|
1144
|
+
}
|
|
1145
|
+
return ` ${propKey({ name: relation.name })}: ${expression};`;
|
|
1146
|
+
}).join(`
|
|
1147
|
+
`);
|
|
1148
|
+
parts.push(`type ${n}ViewRelationResult<K extends keyof ${n}Relations, D> = {
|
|
1149
|
+
${relationCases}
|
|
1150
|
+
}[K];`);
|
|
1151
|
+
parts.push(`type ${n}ViewIncludeResult<A, D> = A extends { readonly include: infer I extends object }
|
|
1152
|
+
` + ` ? { [K in keyof I & keyof ${n}Relations as ${falseFilter("I")}]: ${n}ViewRelationResult<K, D> }
|
|
1153
|
+
` + ` : unknown;`);
|
|
1154
|
+
parts.push(`export type ${n}ViewResult<F, A, D> = ViewRow<${n}ViewSelectScalars<F, A> & ${n}ViewReveal<A> & ${n}ViewIncludeResult<A, D>>;`);
|
|
1155
|
+
} else {
|
|
1156
|
+
parts.push(`export type ${n}ViewResult<F, A, D> = ViewRow<${n}ViewSelectScalars<F, A> & ${n}ViewReveal<A>>;`);
|
|
1157
|
+
}
|
|
1158
|
+
parts.push(`export type ${n}ViewDelegate<F, D extends ViewDefinitionInput = ViewDefinitionInput> = {
|
|
1159
|
+
` + ` findMany<A extends ${n}ViewFindManyArgs = ${n}ViewFindManyArgs>(args?: A): Promise<${n}ViewResult<F, A, D>[]>;
|
|
1160
|
+
` + ` findFirst<A extends ${n}ViewFindManyArgs = ${n}ViewFindManyArgs>(args?: A): Promise<${n}ViewResult<F, A, D> | null>;
|
|
1161
|
+
` + ` findFirstOrThrow<A extends ${n}ViewFindManyArgs = ${n}ViewFindManyArgs>(args?: A): Promise<${n}ViewResult<F, A, D>>;
|
|
1162
|
+
` + ` findUnique<A extends ${n}ViewFindUniqueArgs>(args: A): Promise<${n}ViewResult<F, A, D> | null>;
|
|
1163
|
+
` + ` findUniqueOrThrow<A extends ${n}ViewFindUniqueArgs>(args: A): Promise<${n}ViewResult<F, A, D>>;
|
|
1164
|
+
` + ` count(args?: ${n}CountArgs): Promise<number>;
|
|
1165
|
+
` + ` aggregate<A extends ${n}AggregateArgs>(args: A): Promise<${n}AggregateResult<A>>;
|
|
1166
|
+
` + ` groupBy<A extends ${n}GroupByArgs>(args: A): Promise<${n}GroupByResult<A>[]>;
|
|
1167
|
+
` + `};`);
|
|
1168
|
+
return parts.join(`
|
|
1169
|
+
|
|
1170
|
+
`);
|
|
1171
|
+
}
|
|
1172
|
+
function emitViewSection(params) {
|
|
1173
|
+
const { view } = params;
|
|
1174
|
+
const parts = [banner({ title: "Field-masking views ($defineView)" })];
|
|
1175
|
+
parts.push(`/**
|
|
1176
|
+
` + ` * A masked row: definition fields are properties, JSON.stringify drops
|
|
1177
|
+
` + ` * revealed fields (toJSON), $unsafe() returns everything as a plain object.
|
|
1178
|
+
` + ` */
|
|
1179
|
+
` + `export type ViewRow<T> = T & {
|
|
1180
|
+
` + ` toJSON(): Partial<T>;
|
|
1181
|
+
` + ` $unsafe(): Record<string, unknown>;
|
|
1182
|
+
` + `};`);
|
|
1183
|
+
parts.push(objectAlias({
|
|
1184
|
+
name: "ViewDefinitionInput",
|
|
1185
|
+
lines: view.models.map((model) => ` readonly ${propKey({ name: model.clientName })}?: ${model.name}ViewFields;`)
|
|
1186
|
+
}));
|
|
1187
|
+
parts.push(`type ViewDelegateMap<D extends ViewDefinitionInput> = {
|
|
1188
|
+
${view.models.map((model) => ` ${propKey({ name: model.clientName })}: ${model.name}ViewDelegate<NonNullable<D[${tsString({ value: model.clientName })}]>, D>;`).join(`
|
|
1189
|
+
`)}
|
|
1190
|
+
};`);
|
|
1191
|
+
parts.push(`/** A read-only scoped client over the definition's models \u2014 what $defineView returns. */
|
|
1192
|
+
` + `export type ViewClient<D extends ViewDefinitionInput> = {
|
|
1193
|
+
` + ` readonly [K in keyof D & keyof ViewDelegateMap<D>]: ViewDelegateMap<D>[K];
|
|
1194
|
+
` + `};`);
|
|
1195
|
+
for (const model of view.models) {
|
|
1196
|
+
parts.push(emitViewTypesForModel({ model, view }));
|
|
1197
|
+
}
|
|
1198
|
+
return parts;
|
|
1199
|
+
}
|
|
1200
|
+
function emitClient(params) {
|
|
1201
|
+
const { view, baked, hasComputed } = params;
|
|
1202
|
+
const parts = [banner({ title: "Client" })];
|
|
1203
|
+
parts.push(objectAlias({
|
|
1204
|
+
name: "VibeClientOptions",
|
|
1205
|
+
lines: [
|
|
1206
|
+
` readonly adapter: DatabaseAdapter;`,
|
|
1207
|
+
` readonly onQuery?: (event: QueryEvent) => void;`,
|
|
1208
|
+
` readonly relationStrategy?: RelationStrategy;`,
|
|
1209
|
+
...baked.empty ? [] : [
|
|
1210
|
+
` /** The live extension instances \u2014 verified against the baked manifest. */`,
|
|
1211
|
+
` readonly extensions?: readonly VibeExtension[];`
|
|
1212
|
+
],
|
|
1213
|
+
...hasComputed ? [
|
|
1214
|
+
` /** The live computed-field config \u2014 verified against the baked manifest (VIBE_STALE_CLIENT). */`,
|
|
1215
|
+
` readonly computed: ComputedConfig;`
|
|
1216
|
+
] : []
|
|
1217
|
+
]
|
|
1218
|
+
}));
|
|
1219
|
+
const delegateProps = view.models.map((model) => ` readonly ${propKey({ name: model.clientName })}: ${model.name}Delegate;`);
|
|
1220
|
+
const clientExtensionLines = baked.clientDts.length === 0 ? "" : `${baked.clientDts.join(`
|
|
1221
|
+
`)}
|
|
1222
|
+
`;
|
|
1223
|
+
parts.push(`export type VibeClientInstance = {
|
|
1224
|
+
` + `${delegateProps.join(`
|
|
1225
|
+
`)}
|
|
1226
|
+
` + ` readonly $transaction: <T>(fn: (tx: VibeClientInstance) => Promise<T>, options?: TransactionOptions) => Promise<T>;
|
|
1227
|
+
` + ` readonly $queryRaw: <T = Record<string, unknown>>(strings: TemplateStringsArray, ...values: unknown[]) => Promise<T[]>;
|
|
1228
|
+
` + ` readonly $executeRaw: (strings: TemplateStringsArray, ...values: unknown[]) => Promise<number>;
|
|
1229
|
+
` + ` readonly $queryRawUnsafe: <T = Record<string, unknown>>(text: string, ...values: unknown[]) => Promise<T[]>;
|
|
1230
|
+
` + ` readonly $executeRawUnsafe: (text: string, ...values: unknown[]) => Promise<number>;
|
|
1231
|
+
` + ` readonly $connect: () => Promise<void>;
|
|
1232
|
+
` + ` readonly $disconnect: () => Promise<void>;
|
|
1233
|
+
` + ` /** Field-masking views (v1 defineView parity): a read-only masked projection of models. */
|
|
1234
|
+
` + ` readonly $defineView: <D extends ViewDefinitionInput>(options: { readonly name: string; readonly definition: D }) => ViewClient<D>;
|
|
1235
|
+
` + clientExtensionLines + `};`);
|
|
1236
|
+
parts.push(`/** Build a typed client over a database adapter. */
|
|
1237
|
+
export declare function VibeClient(options: VibeClientOptions): VibeClientInstance;`);
|
|
1238
|
+
parts.push(`/** The normalized Schema IR this client was generated from (plain data). */
|
|
1239
|
+
export declare const schema: SchemaIR;`);
|
|
1240
|
+
return parts.join(`
|
|
1241
|
+
|
|
1242
|
+
`);
|
|
1243
|
+
}
|
|
1244
|
+
function emitDts(params) {
|
|
1245
|
+
const { view } = params;
|
|
1246
|
+
const baked = params.baked ?? EMPTY_BAKED_EXTENSIONS;
|
|
1247
|
+
const computed = params.computed ?? [];
|
|
1248
|
+
const hasComputed = computed.length > 0;
|
|
1249
|
+
const computedByModel = new Map;
|
|
1250
|
+
for (const entry of computed) {
|
|
1251
|
+
const list = computedByModel.get(entry.model) ?? [];
|
|
1252
|
+
list.push(entry);
|
|
1253
|
+
computedByModel.set(entry.model, list);
|
|
1254
|
+
}
|
|
1255
|
+
const parts = [];
|
|
1256
|
+
parts.push(`// Generated by VibeORM (@vibeorm/generator) \u2014 DO NOT EDIT.
|
|
1257
|
+
// Regenerate with \`vibeorm generate\` after every schema change.`);
|
|
1258
|
+
const schemaTypeImports = [
|
|
1259
|
+
"SchemaIR",
|
|
1260
|
+
...baked.empty ? [] : ["VibeExtension"],
|
|
1261
|
+
...hasComputed ? ["ComputedConfig"] : []
|
|
1262
|
+
].sort();
|
|
1263
|
+
const importLines = [
|
|
1264
|
+
`import type { DatabaseAdapter, QueryEvent, TransactionOptions } from "@vibeorm/runtime";`,
|
|
1265
|
+
`import type { ${schemaTypeImports.join(", ")} } from "@vibeorm/schema";`,
|
|
1266
|
+
...baked.dtsImports
|
|
1267
|
+
];
|
|
1268
|
+
parts.push(`${importLines.join(`
|
|
1269
|
+
`)}
|
|
1270
|
+
|
|
1271
|
+
` + `export { VibeError } from "@vibeorm/schema";
|
|
1272
|
+
export type { VibeErrorCode } from "@vibeorm/schema";`);
|
|
1273
|
+
parts.push(PREAMBLE);
|
|
1274
|
+
parts.push(...filterSection());
|
|
1275
|
+
for (const declared of view.enums)
|
|
1276
|
+
parts.push(emitEnum({ declared }));
|
|
1277
|
+
const registry = new Map;
|
|
1278
|
+
const modelParts = [];
|
|
1279
|
+
for (const model of view.models) {
|
|
1280
|
+
const extensionLines = baked.delegateDts.get(model.name);
|
|
1281
|
+
const modelComputed = computedByModel.get(model.name);
|
|
1282
|
+
modelParts.push(emitModel({
|
|
1283
|
+
model,
|
|
1284
|
+
view,
|
|
1285
|
+
registry,
|
|
1286
|
+
...extensionLines === undefined ? {} : { extensionLines },
|
|
1287
|
+
...modelComputed === undefined ? {} : { computed: modelComputed }
|
|
1288
|
+
}));
|
|
1289
|
+
}
|
|
1290
|
+
parts.push(...modelParts);
|
|
1291
|
+
if (registry.size > 0) {
|
|
1292
|
+
parts.push(banner({ title: "Nested write inputs" }));
|
|
1293
|
+
for (const name of [...registry.keys()].sort()) {
|
|
1294
|
+
parts.push(registry.get(name));
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
if (baked.moduleDts.length > 0) {
|
|
1298
|
+
parts.push(banner({ title: "Extensions" }));
|
|
1299
|
+
parts.push(...baked.moduleDts);
|
|
1300
|
+
}
|
|
1301
|
+
parts.push(...emitViewSection({ view }));
|
|
1302
|
+
parts.push(emitClient({ view, baked, hasComputed }));
|
|
1303
|
+
return `${parts.join(`
|
|
1304
|
+
|
|
1305
|
+
`)}
|
|
1306
|
+
`;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
// packages/generator/src/emit-js.ts
|
|
1310
|
+
function emitJs(params) {
|
|
1311
|
+
const { view } = params;
|
|
1312
|
+
const baked = params.baked ?? EMPTY_BAKED_EXTENSIONS;
|
|
1313
|
+
const computed = params.computed ?? [];
|
|
1314
|
+
const hasComputed = computed.length > 0;
|
|
1315
|
+
const parts = [];
|
|
1316
|
+
parts.push(`// Generated by VibeORM (@vibeorm/generator) \u2014 DO NOT EDIT.
|
|
1317
|
+
// Regenerate with \`vibeorm generate\` after every schema change.`);
|
|
1318
|
+
parts.push(`import { createClient } from "@vibeorm/runtime";`);
|
|
1319
|
+
if (baked.jsImports.length > 0)
|
|
1320
|
+
parts.push(baked.jsImports.join(`
|
|
1321
|
+
`));
|
|
1322
|
+
parts.push(`export { VibeError } from "@vibeorm/schema";`);
|
|
1323
|
+
parts.push(`/** The normalized Schema IR this client was generated from (plain data). */
|
|
1324
|
+
export const schema = ${stableStringify({ value: view.schema })};`);
|
|
1325
|
+
for (const declared of view.enums) {
|
|
1326
|
+
const entries = declared.values.map((value) => ` ${tsString({ value })}: ${tsString({ value })},`).join(`
|
|
1327
|
+
`);
|
|
1328
|
+
parts.push(`export const ${declared.name} = Object.freeze({
|
|
1329
|
+
${entries}
|
|
1330
|
+
});`);
|
|
1331
|
+
}
|
|
1332
|
+
if (baked.empty && !hasComputed) {
|
|
1333
|
+
parts.push(`/** Build a typed client over a database adapter. */
|
|
1334
|
+
` + `export function VibeClient(options) {
|
|
1335
|
+
` + ` const { adapter, ...clientOptions } = options;
|
|
1336
|
+
` + ` return createClient({ schema, adapter, options: clientOptions });
|
|
1337
|
+
` + `}`);
|
|
1338
|
+
return `${parts.join(`
|
|
1339
|
+
|
|
1340
|
+
`)}
|
|
1341
|
+
`;
|
|
1342
|
+
}
|
|
1343
|
+
if (!baked.empty) {
|
|
1344
|
+
parts.push(`/** Extension manifest \u2014 verified against the live extension list (VIBE_STALE_CLIENT). */
|
|
1345
|
+
` + `const extensionManifest = ${stableStringify({ value: baked.manifest })};`);
|
|
1346
|
+
parts.push(`/** Baked extension surfaces (extensions.md D9). */
|
|
1347
|
+
` + `const extensionMounts = [
|
|
1348
|
+
${baked.mounts.map((mount) => ` ${mount},`).join(`
|
|
1349
|
+
`)}
|
|
1350
|
+
];`);
|
|
1351
|
+
}
|
|
1352
|
+
if (hasComputed) {
|
|
1353
|
+
parts.push(`/** Computed-field manifest \u2014 verified against the live computed config (VIBE_STALE_CLIENT). */
|
|
1354
|
+
` + `const computedManifest = ${stableStringify({ value: computed })};`);
|
|
1355
|
+
}
|
|
1356
|
+
const destructured = [
|
|
1357
|
+
"adapter",
|
|
1358
|
+
...baked.empty ? [] : ["extensions"],
|
|
1359
|
+
...hasComputed ? ["computed"] : [],
|
|
1360
|
+
"...clientOptions"
|
|
1361
|
+
].join(", ");
|
|
1362
|
+
const createLines = [
|
|
1363
|
+
` schema,`,
|
|
1364
|
+
` adapter,`,
|
|
1365
|
+
` options: clientOptions,`,
|
|
1366
|
+
...baked.empty ? [] : [` extensions: { manifest: extensionManifest, mounts: extensionMounts, instances: extensions ?? [] },`],
|
|
1367
|
+
...hasComputed ? [` computed: { manifest: computedManifest, specs: computed },`] : []
|
|
1368
|
+
];
|
|
1369
|
+
parts.push(`/** Build a typed client over a database adapter. */
|
|
1370
|
+
` + `export function VibeClient(options) {
|
|
1371
|
+
` + ` const { ${destructured} } = options;
|
|
1372
|
+
` + ` return createClient({
|
|
1373
|
+
${createLines.join(`
|
|
1374
|
+
`)}
|
|
1375
|
+
});
|
|
1376
|
+
` + `}`);
|
|
1377
|
+
return `${parts.join(`
|
|
1378
|
+
|
|
1379
|
+
`)}
|
|
1380
|
+
`;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// packages/generator/src/schema-view.ts
|
|
1384
|
+
import { assertValidSchemaIR, isEnumType } from "@vibeorm/schema";
|
|
1385
|
+
function byName(left, right) {
|
|
1386
|
+
return left.name < right.name ? -1 : left.name > right.name ? 1 : 0;
|
|
1387
|
+
}
|
|
1388
|
+
function buildFieldView(params) {
|
|
1389
|
+
const { field } = params;
|
|
1390
|
+
const enumRef = isEnumType(field.type);
|
|
1391
|
+
return {
|
|
1392
|
+
name: field.name,
|
|
1393
|
+
scalar: enumRef ? null : field.type,
|
|
1394
|
+
enumName: enumRef ? field.type.enum : null,
|
|
1395
|
+
isList: field.isList,
|
|
1396
|
+
isOptional: field.isOptional,
|
|
1397
|
+
isId: field.isId,
|
|
1398
|
+
isUnique: field.isUnique,
|
|
1399
|
+
hasDefault: field.default !== undefined,
|
|
1400
|
+
isUpdatedAt: field.isUpdatedAt
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
function resolveInverse(params) {
|
|
1404
|
+
const { relation, target } = params;
|
|
1405
|
+
const candidates = target.relations.filter((candidate) => candidate.targetModel === relation.model && (candidate.relationName ?? null) === (relation.relationName ?? null) && !(candidate.model === relation.model && candidate.name === relation.name));
|
|
1406
|
+
const first = candidates[0];
|
|
1407
|
+
return candidates.length === 1 && first !== undefined ? first.name : null;
|
|
1408
|
+
}
|
|
1409
|
+
function buildUniques(params) {
|
|
1410
|
+
const { model, fields } = params;
|
|
1411
|
+
const fieldByName = new Map(fields.map((field) => [field.name, field]));
|
|
1412
|
+
const members = [];
|
|
1413
|
+
const seen = new Set;
|
|
1414
|
+
const add = (names) => {
|
|
1415
|
+
const key = names.join("_");
|
|
1416
|
+
if (seen.has(key))
|
|
1417
|
+
return;
|
|
1418
|
+
const resolved = [];
|
|
1419
|
+
for (const name of names) {
|
|
1420
|
+
const field = fieldByName.get(name);
|
|
1421
|
+
if (field === undefined)
|
|
1422
|
+
return;
|
|
1423
|
+
resolved.push(field);
|
|
1424
|
+
}
|
|
1425
|
+
seen.add(key);
|
|
1426
|
+
members.push({ key, fields: resolved });
|
|
1427
|
+
};
|
|
1428
|
+
for (const field of fields) {
|
|
1429
|
+
if (field.isId || field.isUnique)
|
|
1430
|
+
add([field.name]);
|
|
1431
|
+
}
|
|
1432
|
+
if (model.primaryKey !== undefined && model.primaryKey.fields.length > 1) {
|
|
1433
|
+
add(model.primaryKey.fields);
|
|
1434
|
+
}
|
|
1435
|
+
for (const index of model.indexes) {
|
|
1436
|
+
if (index.kind === "unique")
|
|
1437
|
+
add(index.fields.map((field) => field.name));
|
|
1438
|
+
}
|
|
1439
|
+
return members;
|
|
1440
|
+
}
|
|
1441
|
+
function buildModelView(params) {
|
|
1442
|
+
const { model, modelIRByName } = params;
|
|
1443
|
+
const fields = model.fields.filter((field) => field.isUnsupported !== true).map((field) => buildFieldView({ field }));
|
|
1444
|
+
const fkFieldNames = new Set;
|
|
1445
|
+
const relations = model.relations.map((relation) => {
|
|
1446
|
+
for (const fk of relation.fields ?? [])
|
|
1447
|
+
fkFieldNames.add(fk);
|
|
1448
|
+
const target = modelIRByName.get(relation.targetModel);
|
|
1449
|
+
return {
|
|
1450
|
+
name: relation.name,
|
|
1451
|
+
target: relation.targetModel,
|
|
1452
|
+
type: relation.type,
|
|
1453
|
+
isList: relation.isList,
|
|
1454
|
+
isOptional: relation.isOptional,
|
|
1455
|
+
fkFields: relation.fields ?? [],
|
|
1456
|
+
inverseName: target === undefined ? null : resolveInverse({ relation, target })
|
|
1457
|
+
};
|
|
1458
|
+
});
|
|
1459
|
+
return {
|
|
1460
|
+
name: model.name,
|
|
1461
|
+
clientName: clientNameOf({ name: model.name }),
|
|
1462
|
+
fields,
|
|
1463
|
+
relations,
|
|
1464
|
+
uniques: buildUniques({ model, fields }),
|
|
1465
|
+
fkFieldNames,
|
|
1466
|
+
isView: model.isView
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
function buildSchemaView(params) {
|
|
1470
|
+
const { schema } = params;
|
|
1471
|
+
assertValidSchemaIR({ schema });
|
|
1472
|
+
const normalized = {
|
|
1473
|
+
datasource: schema.datasource,
|
|
1474
|
+
models: [...schema.models].sort(byName),
|
|
1475
|
+
enums: [...schema.enums].sort(byName),
|
|
1476
|
+
extensions: [...schema.extensions].sort(byName)
|
|
1477
|
+
};
|
|
1478
|
+
const modelIRByName = new Map(normalized.models.map((model) => [model.name, model]));
|
|
1479
|
+
const models = normalized.models.map((model) => buildModelView({ model, modelIRByName }));
|
|
1480
|
+
return {
|
|
1481
|
+
schema: normalized,
|
|
1482
|
+
enums: normalized.enums.map((declared) => ({ name: declared.name, values: declared.values })),
|
|
1483
|
+
models,
|
|
1484
|
+
modelByName: new Map(models.map((model) => [model.name, model]))
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
// packages/generator/src/generate.ts
|
|
1489
|
+
var README = `# Generated VibeORM client
|
|
1490
|
+
|
|
1491
|
+
This directory was generated by \`@vibeorm/generator\` from your VibeORM schema \u2014 do not edit it by hand, any change is overwritten on the next generate. After every schema change, regenerate with \`vibeorm generate\` (or programmatically via \`generateClient({ schema, outDir })\` from \`@vibeorm/generator\`).
|
|
1492
|
+
`;
|
|
1493
|
+
async function generateClient(params) {
|
|
1494
|
+
const { schema, outDir } = params;
|
|
1495
|
+
const view = buildSchemaView({ schema });
|
|
1496
|
+
const baked = bakeExtensions({ schema: view.schema, extensions: params.extensions ?? [] });
|
|
1497
|
+
let computed = [];
|
|
1498
|
+
if (params.computed !== undefined && Object.keys(params.computed).length > 0) {
|
|
1499
|
+
validateComputedConfig({ schema, computed: params.computed });
|
|
1500
|
+
computed = buildComputedManifest({ computed: params.computed });
|
|
1501
|
+
}
|
|
1502
|
+
const outputs = [
|
|
1503
|
+
{ name: "index.js", content: emitJs({ view, baked, computed }) },
|
|
1504
|
+
{ name: "index.d.ts", content: emitDts({ view, baked, computed }) },
|
|
1505
|
+
{ name: "README.md", content: README }
|
|
1506
|
+
];
|
|
1507
|
+
await mkdir(outDir, { recursive: true });
|
|
1508
|
+
const files = [];
|
|
1509
|
+
for (const output of outputs) {
|
|
1510
|
+
const path = join(outDir, output.name);
|
|
1511
|
+
await writeFile(path, output.content, "utf-8");
|
|
1512
|
+
files.push(path);
|
|
1513
|
+
}
|
|
1514
|
+
return { files };
|
|
1515
|
+
}
|
|
1516
|
+
export {
|
|
1517
|
+
generateClient,
|
|
1518
|
+
bakeExtensions
|
|
1519
|
+
};
|
|
1520
|
+
|
|
1521
|
+
//# debugId=793CBF3FCF2EA78F64756E2164756E21
|