@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.
@@ -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
+ }