@vibeorm/generator 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -0
- package/package.json +42 -0
- package/src/generate.ts +65 -0
- package/src/generators/generate-args.ts +322 -0
- package/src/generators/generate-client.ts +186 -0
- package/src/generators/generate-delegates.ts +213 -0
- package/src/generators/generate-enums.ts +28 -0
- package/src/generators/generate-inputs.ts +626 -0
- package/src/generators/generate-models.ts +103 -0
- package/src/generators/generate-result.ts +163 -0
- package/src/generators/generate-schemas.ts +474 -0
- package/src/index.ts +2 -0
- package/src/utils.ts +51 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { Model, Schema, Field, ScalarField, EnumField, RelationField } from "@vibeorm/parser";
|
|
2
|
+
import { fileHeader, toCamelCase } from "../utils.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generates the Payload types for each model.
|
|
6
|
+
* These split each model into { scalars, objects, composites }
|
|
7
|
+
* which is the foundation for select/include type narrowing.
|
|
8
|
+
*/
|
|
9
|
+
export function generateModels(params: { schema: Schema }): string {
|
|
10
|
+
const { schema } = params;
|
|
11
|
+
const parts: string[] = [fileHeader()];
|
|
12
|
+
|
|
13
|
+
// Import enums if any
|
|
14
|
+
if (schema.enums.length > 0) {
|
|
15
|
+
const enumImports = schema.enums.map((e) => e.name).join(", ");
|
|
16
|
+
parts.push(`import type { ${enumImports} } from "./enums.ts";\n`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Generate JsonValue type
|
|
20
|
+
parts.push(`export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };\n`);
|
|
21
|
+
|
|
22
|
+
// Generate the base OperationPayload interface
|
|
23
|
+
parts.push(`export type OperationPayload = {
|
|
24
|
+
name: string;
|
|
25
|
+
scalars: Record<string, unknown>;
|
|
26
|
+
objects: Record<string, unknown>;
|
|
27
|
+
composites: Record<string, unknown>;
|
|
28
|
+
};\n`);
|
|
29
|
+
|
|
30
|
+
// Generate payload type for each model
|
|
31
|
+
for (const model of schema.models) {
|
|
32
|
+
parts.push(generatePayloadType({ model, schema }));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Generate plain model types (default selection = scalars only)
|
|
36
|
+
for (const model of schema.models) {
|
|
37
|
+
parts.push(generatePlainModelType({ model, schema }));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return parts.join("\n");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function generatePayloadType(params: { model: Model; schema: Schema }): string {
|
|
44
|
+
const { model, schema } = params;
|
|
45
|
+
|
|
46
|
+
const scalarFields = model.fields.filter(
|
|
47
|
+
(f): f is ScalarField | EnumField => f.kind === "scalar" || f.kind === "enum"
|
|
48
|
+
);
|
|
49
|
+
const relationFields = model.fields.filter(
|
|
50
|
+
(f): f is RelationField => f.kind === "relation"
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// Scalars block
|
|
54
|
+
const scalarsEntries = scalarFields.map((f) => {
|
|
55
|
+
const tsType = f.kind === "scalar" ? f.tsType : f.enumName;
|
|
56
|
+
const nullable = f.isRequired ? "" : " | null";
|
|
57
|
+
const listPrefix = f.isList ? "" : "";
|
|
58
|
+
const listSuffix = f.isList ? "[]" : "";
|
|
59
|
+
return ` ${f.name}: ${listPrefix}${tsType}${nullable}${listSuffix};`;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Objects block
|
|
63
|
+
const objectsEntries = relationFields.map((f) => {
|
|
64
|
+
const payloadType = `$${f.relatedModel}Payload`;
|
|
65
|
+
if (f.isList) {
|
|
66
|
+
return ` ${f.name}: ${payloadType}[];`;
|
|
67
|
+
}
|
|
68
|
+
const nullable = f.isRequired ? "" : " | null";
|
|
69
|
+
return ` ${f.name}: ${payloadType}${nullable};`;
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return `export type $${model.name}Payload = {
|
|
73
|
+
name: "${model.name}";
|
|
74
|
+
scalars: {
|
|
75
|
+
${scalarsEntries.join("\n")}
|
|
76
|
+
};
|
|
77
|
+
objects: {
|
|
78
|
+
${objectsEntries.join("\n")}
|
|
79
|
+
};
|
|
80
|
+
composites: {};
|
|
81
|
+
};
|
|
82
|
+
`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function generatePlainModelType(params: { model: Model; schema: Schema }): string {
|
|
86
|
+
const { model } = params;
|
|
87
|
+
|
|
88
|
+
const scalarFields = model.fields.filter(
|
|
89
|
+
(f): f is ScalarField | EnumField => f.kind === "scalar" || f.kind === "enum"
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const entries = scalarFields.map((f) => {
|
|
93
|
+
const tsType = f.kind === "scalar" ? f.tsType : f.enumName;
|
|
94
|
+
const nullable = f.isRequired ? "" : " | null";
|
|
95
|
+
const listSuffix = f.isList ? "[]" : "";
|
|
96
|
+
return ` ${f.name}: ${tsType}${nullable}${listSuffix};`;
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return `export type ${model.name} = {
|
|
100
|
+
${entries.join("\n")}
|
|
101
|
+
};
|
|
102
|
+
`;
|
|
103
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { fileHeader } from "../utils.ts";
|
|
2
|
+
import type { Schema } from "@vibeorm/parser";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generates the core type resolution engine.
|
|
6
|
+
*
|
|
7
|
+
* This produces the conditional types that narrow return types
|
|
8
|
+
* based on the select/include args passed to operations.
|
|
9
|
+
*
|
|
10
|
+
* The pattern:
|
|
11
|
+
* - No select/include → DefaultSelection (scalars only)
|
|
12
|
+
* - select: { field: true } → only selected fields
|
|
13
|
+
* - include: { relation: true } → scalars + included relations
|
|
14
|
+
* - Nested select/include in relations → recursive narrowing
|
|
15
|
+
*/
|
|
16
|
+
export function generateResult(params: { schema: Schema }): string {
|
|
17
|
+
const { schema } = params;
|
|
18
|
+
const parts: string[] = [fileHeader()];
|
|
19
|
+
|
|
20
|
+
// Import payload types
|
|
21
|
+
const payloadImports = schema.models
|
|
22
|
+
.map((m) => `$${m.name}Payload`)
|
|
23
|
+
.join(", ");
|
|
24
|
+
parts.push(
|
|
25
|
+
`import type { ${payloadImports}, OperationPayload } from "./models.ts";\n`
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
// ─── Utility Types ────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
parts.push(`
|
|
31
|
+
// ─── Core Utility Types ───────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
/** Detect if T is exactly \`any\` */
|
|
34
|
+
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
35
|
+
|
|
36
|
+
/** Flatten intersection types for cleaner hover tooltips */
|
|
37
|
+
type Compute<T> = T extends Function ? T : { [K in keyof T]: T[K] } & unknown;
|
|
38
|
+
|
|
39
|
+
/** Extract scalar fields from a payload (default selection) */
|
|
40
|
+
type DefaultSelection<P extends OperationPayload> = P["scalars"];
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Check if a key exists in the payload's objects or composites
|
|
44
|
+
* and extract the value type.
|
|
45
|
+
*/
|
|
46
|
+
type SelectableObject<P extends OperationPayload, K extends PropertyKey> =
|
|
47
|
+
K extends keyof P["objects"] ? P["objects"][K] : never;
|
|
48
|
+
|
|
49
|
+
// ─── GetFindResult ────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The core type that resolves the result type based on select/include args.
|
|
53
|
+
*
|
|
54
|
+
* - If no args or args is any → DefaultSelection (scalars only)
|
|
55
|
+
* - If select is provided → only selected fields (scalars + expanded relations)
|
|
56
|
+
* - If include is provided → all scalars + included relations
|
|
57
|
+
*/
|
|
58
|
+
export type GetFindResult<P extends OperationPayload, A> =
|
|
59
|
+
// If Args is any (no args passed), return default
|
|
60
|
+
IsAny<A> extends true
|
|
61
|
+
? DefaultSelection<P>
|
|
62
|
+
: A extends { select: infer S extends Record<string, unknown> }
|
|
63
|
+
? // ── SELECT branch ──
|
|
64
|
+
Compute<{
|
|
65
|
+
[K in keyof S as S[K] extends false | undefined | null
|
|
66
|
+
? never
|
|
67
|
+
: K]: K extends keyof P["scalars"]
|
|
68
|
+
? // Scalar field selected
|
|
69
|
+
P["scalars"][K]
|
|
70
|
+
: K extends keyof P["objects"]
|
|
71
|
+
? // Relation field selected
|
|
72
|
+
P["objects"][K] extends OperationPayload[]
|
|
73
|
+
? // Array relation
|
|
74
|
+
S[K] extends Record<string, unknown>
|
|
75
|
+
? GetFindResult<P["objects"][K][number], S[K]>[]
|
|
76
|
+
: DefaultSelection<P["objects"][K][number]>[]
|
|
77
|
+
: P["objects"][K] extends OperationPayload | null
|
|
78
|
+
? // Nullable single relation
|
|
79
|
+
S[K] extends Record<string, unknown>
|
|
80
|
+
? GetFindResult<NonNullable<P["objects"][K]> & OperationPayload, S[K]> | null
|
|
81
|
+
: DefaultSelection<NonNullable<P["objects"][K]> & OperationPayload> | null
|
|
82
|
+
: P["objects"][K] extends OperationPayload
|
|
83
|
+
? // Required single relation
|
|
84
|
+
S[K] extends Record<string, unknown>
|
|
85
|
+
? GetFindResult<P["objects"][K], S[K]>
|
|
86
|
+
: DefaultSelection<P["objects"][K]>
|
|
87
|
+
: never
|
|
88
|
+
: never;
|
|
89
|
+
}>
|
|
90
|
+
: A extends { include: infer I extends Record<string, unknown> }
|
|
91
|
+
? // ── INCLUDE branch ──
|
|
92
|
+
// All scalars + included relations
|
|
93
|
+
Compute<
|
|
94
|
+
DefaultSelection<P> & {
|
|
95
|
+
[K in keyof I as I[K] extends false | undefined | null
|
|
96
|
+
? never
|
|
97
|
+
: K]: K extends keyof P["objects"]
|
|
98
|
+
? P["objects"][K] extends OperationPayload[]
|
|
99
|
+
? // Array relation
|
|
100
|
+
I[K] extends Record<string, unknown>
|
|
101
|
+
? GetFindResult<P["objects"][K][number], I[K]>[]
|
|
102
|
+
: DefaultSelection<P["objects"][K][number]>[]
|
|
103
|
+
: P["objects"][K] extends OperationPayload | null
|
|
104
|
+
? // Nullable single relation
|
|
105
|
+
I[K] extends Record<string, unknown>
|
|
106
|
+
? GetFindResult<NonNullable<P["objects"][K]> & OperationPayload, I[K]> | null
|
|
107
|
+
: DefaultSelection<NonNullable<P["objects"][K]> & OperationPayload> | null
|
|
108
|
+
: P["objects"][K] extends OperationPayload
|
|
109
|
+
? // Required single relation
|
|
110
|
+
I[K] extends Record<string, unknown>
|
|
111
|
+
? GetFindResult<P["objects"][K], I[K]>
|
|
112
|
+
: DefaultSelection<P["objects"][K]>
|
|
113
|
+
: never
|
|
114
|
+
: never;
|
|
115
|
+
}
|
|
116
|
+
>
|
|
117
|
+
: // ── No select/include ──
|
|
118
|
+
DefaultSelection<P>;
|
|
119
|
+
|
|
120
|
+
// ─── Operation Result Types ───────────────────────────────────────
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Maps an operation name to the correct result cardinality.
|
|
124
|
+
*/
|
|
125
|
+
export type GetResult<
|
|
126
|
+
P extends OperationPayload,
|
|
127
|
+
A,
|
|
128
|
+
Op extends string,
|
|
129
|
+
> = {
|
|
130
|
+
findMany: GetFindResult<P, A>[];
|
|
131
|
+
findFirst: GetFindResult<P, A> | null;
|
|
132
|
+
findUnique: GetFindResult<P, A> | null;
|
|
133
|
+
findUniqueOrThrow: GetFindResult<P, A>;
|
|
134
|
+
findFirstOrThrow: GetFindResult<P, A>;
|
|
135
|
+
create: GetFindResult<P, A>;
|
|
136
|
+
createMany: { count: number };
|
|
137
|
+
createManyAndReturn: GetFindResult<P, A>[];
|
|
138
|
+
update: GetFindResult<P, A>;
|
|
139
|
+
upsert: GetFindResult<P, A>;
|
|
140
|
+
delete: GetFindResult<P, A>;
|
|
141
|
+
count: number;
|
|
142
|
+
deleteMany: { count: number };
|
|
143
|
+
updateMany: { count: number };
|
|
144
|
+
}[Op & keyof {
|
|
145
|
+
findMany: unknown;
|
|
146
|
+
findFirst: unknown;
|
|
147
|
+
findUnique: unknown;
|
|
148
|
+
findUniqueOrThrow: unknown;
|
|
149
|
+
findFirstOrThrow: unknown;
|
|
150
|
+
create: unknown;
|
|
151
|
+
createMany: unknown;
|
|
152
|
+
createManyAndReturn: unknown;
|
|
153
|
+
update: unknown;
|
|
154
|
+
upsert: unknown;
|
|
155
|
+
delete: unknown;
|
|
156
|
+
count: unknown;
|
|
157
|
+
deleteMany: unknown;
|
|
158
|
+
updateMany: unknown;
|
|
159
|
+
}];
|
|
160
|
+
`);
|
|
161
|
+
|
|
162
|
+
return parts.join("\n");
|
|
163
|
+
}
|