@an5/orm 1.0.8 → 1.1.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.
Files changed (73) hide show
  1. package/README.md +174 -175
  2. package/dist/an5Metadata.js +64 -0
  3. package/dist/an5Orm.d.ts +92 -3
  4. package/dist/an5Orm.js +697 -206
  5. package/dist/config.d.ts +9 -1
  6. package/dist/config.js +13 -18
  7. package/dist/errors.d.ts +1 -1
  8. package/dist/errors.js +1 -1
  9. package/dist/generator/src/api.d.ts +35 -0
  10. package/dist/generator/src/api.js +58 -0
  11. package/dist/generator/src/code-generator.d.ts +14 -13
  12. package/dist/generator/src/code-generator.js +249 -225
  13. package/dist/generator/src/config.d.ts +162 -0
  14. package/dist/generator/src/config.js +354 -0
  15. package/dist/generator/src/dialect.d.ts +88 -0
  16. package/dist/generator/src/dialect.js +318 -0
  17. package/dist/generator/src/dotnet-generator.d.ts +21 -11
  18. package/dist/generator/src/dotnet-generator.js +1053 -546
  19. package/dist/generator/src/field-types.d.ts +96 -0
  20. package/dist/generator/src/field-types.js +433 -0
  21. package/dist/generator/src/golang-generator.d.ts +36 -0
  22. package/dist/generator/src/golang-generator.js +1229 -0
  23. package/dist/generator/src/index.d.ts +1 -1
  24. package/dist/generator/src/index.js +137 -88
  25. package/dist/generator/src/metadata-generator.d.ts +11 -8
  26. package/dist/generator/src/metadata-generator.js +105 -90
  27. package/dist/generator/src/parser.d.ts +37 -11
  28. package/dist/generator/src/parser.js +208 -206
  29. package/dist/generator/src/push-apply.d.ts +37 -0
  30. package/dist/generator/src/push-apply.js +109 -0
  31. package/dist/generator/src/push-schema.d.ts +93 -0
  32. package/dist/generator/src/push-schema.js +189 -0
  33. package/dist/generator/src/python-generator.d.ts +37 -9
  34. package/dist/generator/src/python-generator.js +428 -105
  35. package/dist/generator/src/rust-generator.d.ts +38 -0
  36. package/dist/generator/src/rust-generator.js +971 -0
  37. package/dist/generator/src/suggest.d.ts +17 -0
  38. package/dist/generator/src/suggest.js +45 -0
  39. package/dist/generator/src/type-kinds.d.ts +27 -0
  40. package/dist/generator/src/type-kinds.js +72 -0
  41. package/dist/generator/src/types.d.ts +60 -27
  42. package/dist/generator/src/types.js +41 -2
  43. package/dist/index.d.ts +3 -2
  44. package/dist/index.js +19 -8
  45. package/dist/logger.js +1 -1
  46. package/dist/migration-core.d.ts +102 -0
  47. package/dist/migration-core.js +731 -0
  48. package/dist/orm.d.ts +147 -0
  49. package/dist/orm.js +1654 -0
  50. package/dist/provider-support.d.ts +27 -0
  51. package/dist/provider-support.js +50 -0
  52. package/dist/scripts/release-cli.d.ts +2 -2
  53. package/dist/scripts/release-cli.js +115 -115
  54. package/dist/sql-utils.d.ts +3 -1
  55. package/dist/sql-utils.js +94 -24
  56. package/generator/src/api.ts +45 -0
  57. package/generator/src/code-generator.ts +259 -230
  58. package/generator/src/config.ts +496 -0
  59. package/generator/src/dialect.ts +415 -0
  60. package/generator/src/dotnet-generator.ts +1071 -544
  61. package/generator/src/field-types.ts +473 -0
  62. package/generator/src/golang-generator.ts +1267 -0
  63. package/generator/src/index.ts +146 -116
  64. package/generator/src/metadata-generator.ts +116 -101
  65. package/generator/src/parser.ts +234 -202
  66. package/generator/src/push-apply.ts +148 -0
  67. package/generator/src/push-schema.ts +250 -0
  68. package/generator/src/python-generator.ts +480 -114
  69. package/generator/src/rust-generator.ts +974 -0
  70. package/generator/src/suggest.ts +41 -0
  71. package/generator/src/type-kinds.ts +78 -0
  72. package/generator/src/types.ts +77 -29
  73. package/package.json +32 -6
@@ -1,230 +1,259 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { Model, Field, Relation } from './types';
4
-
5
- export class CodeGenerator {
6
- constructor(private outputDir: string) {
7
- if (!fs.existsSync(this.outputDir)) {
8
- fs.mkdirSync(this.outputDir, { recursive: true });
9
- }
10
- }
11
-
12
- public generate(models: Model[]) {
13
- this.generateBaseTs();
14
- for (const model of models) {
15
- this.generateModelFile(model);
16
- }
17
- this.generateIndexTs(models);
18
- }
19
-
20
- private generateBaseTs() {
21
- const baseTs = `
22
- import { An5ClientKnownRequestError as An5KnownError } from "@an5/orm";
23
-
24
- export namespace An5 {
25
- export const An5ClientKnownRequestError: typeof An5KnownError = An5KnownError;
26
- export type An5ClientKnownRequestError = An5KnownError;
27
- export type SortOrder = 'asc' | 'desc';
28
- export type StringFilter = { equals?: string; in?: string[]; notIn?: string[]; lt?: string; lte?: string; gt?: string; gte?: string; contains?: string; startsWith?: string; endsWith?: string; not?: string | StringFilter; };
29
- export type StringNullableFilter = { equals?: string | null; in?: (string | null)[]; notIn?: (string | null)[]; lt?: string; lte?: string; gt?: string; gte?: string; contains?: string; startsWith?: string; endsWith?: string; not?: string | StringNullableFilter | null; };
30
- export type NumberFilter = { equals?: number; in?: number[]; notIn?: number[]; lt?: number; lte?: number; gt?: number; gte?: number; not?: number | NumberFilter; };
31
- export type IntFieldUpdateOperationsInput = { set?: number; increment?: number; decrement?: number; multiply?: number; divide?: number; };
32
- export type FloatFieldUpdateOperationsInput = { set?: number; increment?: number; decrement?: number; multiply?: number; divide?: number; };
33
- export type NumberNullableFilter = { equals?: number | null; in?: (number | null)[]; notIn?: (number | null)[]; lt?: number; lte?: number; gt?: number; gte?: number; not?: number | NumberNullableFilter | null; };
34
- export type BooleanFilter = { equals?: boolean; not?: boolean | BooleanFilter; };
35
- export type BooleanNullableFilter = { equals?: boolean | null; not?: boolean | BooleanNullableFilter | null; };
36
- export type DateTimeFilter = { equals?: Date; in?: Date[]; notIn?: Date[]; lt?: Date; lte?: Date; gt?: Date; gte?: Date; not?: Date | DateTimeFilter; };
37
- export type DateTimeNullableFilter = { equals?: Date | null; in?: (Date | null)[]; notIn?: (Date | null)[]; lt?: Date; lte?: Date; gt?: Date; gte?: Date; not?: Date | DateTimeNullableFilter | null; };
38
- }
39
-
40
- export interface TableClient<T, WhereInput = any, Select = any, Include = any, CreateInput = any, UpdateInput = any, FindManyArgs = any, FindFirstArgs = any, FindUniqueArgs = any, CreateArgs = any, UpdateArgs = any, UpsertArgs = any, DeleteArgs = any> {
41
- findMany(args?: FindManyArgs): Promise<T[]>;
42
- vectorSearch(args: { vector: number[]; take?: number; where?: WhereInput; include?: Include; vectorField?: string; distanceMetric?: 'cosine' | 'euclidean' | 'dot'; }): Promise<(T & { distance: number })[]>;
43
- findFirst(args?: FindFirstArgs): Promise<T | null>;
44
- findUnique(args?: FindUniqueArgs): Promise<T | null>;
45
- count(args?: { where?: WhereInput; }): Promise<number>;
46
- create(args: CreateArgs): Promise<T>;
47
- createMany(args: { data: CreateInput[]; skipDuplicates?: boolean; }): Promise<{ count: number }>;
48
- update(args: UpdateArgs): Promise<T>;
49
- updateMany(args: { where?: WhereInput; data: UpdateInput; }): Promise<{ count: number }>;
50
- delete(args: DeleteArgs): Promise<T>;
51
- deleteMany(args?: { where?: WhereInput; }): Promise<{ count: number }>;
52
- aggregate(args: any): Promise<any>;
53
- groupBy(args: any): Promise<any[]>;
54
- upsert(args: UpsertArgs): Promise<T>;
55
- }
56
- `;
57
- fs.writeFileSync(path.join(this.outputDir, 'base.ts'), baseTs);
58
- }
59
-
60
- private generateModelFile(model: Model) {
61
- let content = `import { An5, TableClient } from './base';\n`;
62
- const relModels = [...new Set(model.relations.map(r => r.type))];
63
- for (const relModel of relModels) {
64
- if (relModel !== model.name) {
65
- content += `import { ${relModel}, ${relModel}WhereInput, ${relModel}FindManyArgs, ${relModel}CreateInput, ${relModel}UpdateInput } from './${relModel}';\n`;
66
- }
67
- }
68
-
69
- content += `\nexport interface ${model.name} {\n`;
70
- for (const field of model.fields) {
71
- content += ` ${field.name}: ${this.normalizeType(field.type)}${field.isOptional ? ' | null' : ''};\n`;
72
- }
73
- for (const rel of model.relations) {
74
- content += ` ${rel.name}?: ${rel.type}${rel.isArray ? '[]' : ''}${rel.isOptional ? ' | null' : ''};\n`;
75
- }
76
- if (model.relations.length > 0) {
77
- content += ` _count?: { ${model.relations.map(r => `${r.name}: number`).join('; ')} };\n`;
78
- }
79
- content += `}\n\n`;
80
-
81
- content += `export type ${model.name}WhereInput = {\n`;
82
- content += ` AND?: ${model.name}WhereInput | ${model.name}WhereInput[];\n`;
83
- content += ` OR?: ${model.name}WhereInput[];\n`;
84
- content += ` NOT?: ${model.name}WhereInput | ${model.name}WhereInput[];\n`;
85
- for (const field of model.fields) {
86
- const filter = this.getFieldFilterType(field);
87
- content += ` ${field.name}?: ${field.type} | ${filter}${field.isOptional ? ' | null' : ''};\n`;
88
- }
89
- for (const rel of model.relations) {
90
- if (rel.isArray) {
91
- content += ` ${rel.name}?: { some?: ${rel.type}WhereInput; none?: ${rel.type}WhereInput; every?: ${rel.type}WhereInput; };\n`;
92
- } else {
93
- content += ` ${rel.name}?: ${rel.type}WhereInput${rel.isOptional ? ' | null' : ''};\n`;
94
- }
95
- }
96
- if (model.compoundUniques) {
97
- for (const compound of model.compoundUniques) {
98
- const keyName = compound.join('_');
99
- content += ` ${keyName}?: { ${compound.map(f => `${f}: ${model.fields.find(mf => mf.name === f)?.type || 'any'}`).join('; ')} };\n`;
100
- }
101
- }
102
- content += `};\n\n`;
103
-
104
- const selectFields = model.fields.map(f => `${f.name}?: boolean`);
105
- const selectRels = model.relations.map(r => `${r.name}?: boolean | ${r.type}FindManyArgs`);
106
- if (model.relations.length > 0) {
107
- selectRels.push(`_count?: boolean | { select?: { ${model.relations.map(r => `${r.name}?: boolean`).join('; ')} } }`);
108
- }
109
- content += `export type ${model.name}Select = { ${[...selectFields, ...selectRels].join('; ')}${[...selectFields, ...selectRels].length > 0 ? ';' : ''} };\n`;
110
-
111
- const includeRels = model.relations.map(r => `${r.name}?: boolean | ${r.type}FindManyArgs`);
112
- if (model.relations.length > 0) {
113
- includeRels.push(`_count?: boolean | { select?: { ${model.relations.map(r => `${r.name}?: boolean`).join('; ')} } }`);
114
- }
115
- content += `export type ${model.name}Include = { ${includeRels.join('; ')}${includeRels.length > 0 ? ';' : ''} };\n`;
116
-
117
- content += `export type ${model.name}CreateInput = { ${model.fields.map(f => `${f.name}${f.isOptional || f.hasDefault ? '?' : ''}: ${f.type}${f.isOptional ? ' | null' : ''}`).join('; ')}; ${model.relations.map(r => `${r.name}?: { create?: ${r.type}CreateInput | ${r.type}CreateInput[]; connect?: ${r.type}WhereInput | ${r.type}WhereInput[]; }`).join('; ')} };\n`;
118
- content += `export type ${model.name}UpdateInput = { ${model.fields.filter(f => !f.isId).map(f => `${f.name}?: ${f.type === 'number' ? 'number | An5.IntFieldUpdateOperationsInput' : f.type}${f.isOptional ? ' | null' : ''}`).join('; ')}; ${model.relations.map(r => `${r.name}?: { create?: ${r.type}CreateInput | ${r.type}CreateInput[]; connect?: ${r.type}WhereInput | ${r.type}WhereInput[]; set?: ${r.type}WhereInput | ${r.type}WhereInput[]; disconnect?: ${r.type}WhereInput | ${r.type}WhereInput[]; delete?: ${r.type}WhereInput | ${r.type}WhereInput[]; update?: { where: ${r.type}WhereInput; data: ${r.type}UpdateInput; } | { where: ${r.type}WhereInput; data: ${r.type}UpdateInput; }[]; upsert?: { where: ${r.type}WhereInput; create: ${r.type}CreateInput; update: ${r.type}UpdateInput; } | { where: ${r.type}WhereInput; create: ${r.type}CreateInput; update: ${r.type}UpdateInput; }[]; }`).join('; ')} };\n`;
119
-
120
- content += `export type ${model.name}FindManyArgs = { where?: ${model.name}WhereInput; orderBy?: any; take?: number; skip?: number; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
121
- content += `export type ${model.name}FindFirstArgs = { where?: ${model.name}WhereInput; orderBy?: any; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
122
- content += `export type ${model.name}FindUniqueArgs = { where?: ${model.name}WhereInput; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
123
- content += `export type ${model.name}CreateArgs = { data: ${model.name}CreateInput; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
124
- content += `export type ${model.name}UpdateArgs = { where: ${model.name}WhereInput; data: ${model.name}UpdateInput; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
125
- content += `export type ${model.name}UpsertArgs = { where: ${model.name}WhereInput; create: ${model.name}CreateInput; update: ${model.name}UpdateInput; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
126
- content += `export type ${model.name}DeleteArgs = { where: ${model.name}WhereInput; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
127
-
128
- content += `export type ${model.name}TableClient = TableClient<\n ${model.name},\n ${model.name}WhereInput,\n ${model.name}Select,\n ${model.name}Include,\n ${model.name}CreateInput,\n ${model.name}UpdateInput,\n ${model.name}FindManyArgs,\n ${model.name}FindFirstArgs,\n ${model.name}FindUniqueArgs,\n ${model.name}CreateArgs,\n ${model.name}UpdateArgs,\n ${model.name}UpsertArgs,\n ${model.name}DeleteArgs\n>;\n`;
129
-
130
- fs.writeFileSync(path.join(this.outputDir, `${model.name}.ts`), content);
131
- }
132
-
133
- private generateIndexTs(models: Model[]) {
134
- let content = `export * from './base';\n`;
135
- for (const model of models) {
136
- content += `export * from './${model.name}';\n`;
137
- }
138
- content += `\nimport { An5 as BaseAn5 } from './base';\n`;
139
- for (const model of models) {
140
- content += `import * as ${model.name}Types from './${model.name}';\n`;
141
- }
142
-
143
- content += `\nexport namespace An5 {\n`;
144
- content += ` export type SortOrder = BaseAn5.SortOrder;\n`;
145
- content += ` export type StringFilter = BaseAn5.StringFilter;\n`;
146
- content += ` export type StringNullableFilter = BaseAn5.StringNullableFilter;\n`;
147
- content += ` export type NumberFilter = BaseAn5.NumberFilter;\n`;
148
- content += ` export type NumberNullableFilter = BaseAn5.NumberNullableFilter;\n`;
149
- content += ` export type BooleanFilter = BaseAn5.BooleanFilter;\n`;
150
- content += ` export type BooleanNullableFilter = BaseAn5.BooleanNullableFilter;\n`;
151
- content += ` export type DateTimeFilter = BaseAn5.DateTimeFilter;\n`;
152
- content += ` export type DateTimeNullableFilter = BaseAn5.DateTimeNullableFilter;\n`;
153
- content += ` export type IntFieldUpdateOperationsInput = BaseAn5.IntFieldUpdateOperationsInput;\n`;
154
- content += ` export type FloatFieldUpdateOperationsInput = BaseAn5.FloatFieldUpdateOperationsInput;\n`;
155
- content += ` export const An5ClientKnownRequestError = BaseAn5.An5ClientKnownRequestError;\n`;
156
-
157
- for (const model of models) {
158
- content += ` export type ${model.name} = ${model.name}Types.${model.name};\n`;
159
- content += ` export type ${model.name}WhereInput = ${model.name}Types.${model.name}WhereInput;\n`;
160
- content += ` export type ${model.name}Select = ${model.name}Types.${model.name}Select;\n`;
161
- content += ` export type ${model.name}Include = ${model.name}Types.${model.name}Include;\n`;
162
- content += ` export type ${model.name}CreateInput = ${model.name}Types.${model.name}CreateInput;\n`;
163
- content += ` export type ${model.name}UpdateInput = ${model.name}Types.${model.name}UpdateInput;\n`;
164
- }
165
- content += `}\n`;
166
-
167
- content += `\nexport class An5Client {\n $connect(): Promise<void> { return Promise.resolve(); }\n $disconnect(): Promise<void> { return Promise.resolve(); }\n $transaction<R>(fn: (tx: An5Client) => Promise<R>, options?: { timeout?: number }): Promise<R>;\n $transaction<R>(list: Promise<R>[]): Promise<R[]>;\n $transaction(fn: any, options?: any): Promise<any> { return typeof fn === 'function' ? fn(this) : Promise.all(fn); }\n $queryRaw<T = any>(queryParts: TemplateStringsArray | string, ...values: any[]): Promise<T> { return Promise.resolve([] as any); }\n $queryRawUnsafe<R = any>(query: string, ...values: any[]): Promise<R> { return Promise.resolve([] as any); }\n $executeRaw<T = any>(queryParts: TemplateStringsArray | string, ...values: any[]): Promise<any> { return Promise.resolve(0 as any); }\n $executeRawUnsafe(query: string, ...values: any[]): Promise<number> { return Promise.resolve(0); }\n`;
168
-
169
- const addedProps = new Set<string>();
170
- for (const model of models) {
171
- const props = this.getAllPropertyVariations(model.name);
172
- for (const prop of props) {
173
- if (!addedProps.has(prop)) {
174
- content += ` ${prop}!: ${model.name}Types.${model.name}TableClient;\n`;
175
- addedProps.add(prop);
176
- }
177
- }
178
- }
179
-
180
- content += `}\n`;
181
- fs.writeFileSync(path.join(this.outputDir, 'index.ts'), content);
182
- }
183
-
184
- private getAllPropertyVariations(modelName: string): string[] {
185
- const variations = new Set<string>();
186
- variations.add(this.toCamelCase(modelName));
187
- const acronyms = ['LLM', 'AI', 'MCP', 'IT', 'QC', 'HR', 'MR', 'WH', 'SSIS', 'API', 'URL', 'ID', 'JSON'];
188
- for (const acronym of acronyms) {
189
- if (modelName.startsWith(acronym)) {
190
- variations.add(acronym.toLowerCase() + modelName.slice(acronym.length));
191
- variations.add(acronym[0].toLowerCase() + acronym.slice(1) + modelName.slice(acronym.length));
192
- }
193
- }
194
- return Array.from(variations);
195
- }
196
-
197
- private getFieldFilterType(field: Field): string {
198
- const normalizedType = this.normalizeType(field.type);
199
- if (normalizedType === 'string') return field.isOptional ? 'An5.StringNullableFilter' : 'An5.StringFilter';
200
- if (normalizedType === 'number') return field.isOptional ? 'An5.NumberNullableFilter' : 'An5.NumberFilter';
201
- if (normalizedType === 'boolean') return field.isOptional ? 'An5.BooleanNullableFilter' : 'An5.BooleanFilter';
202
- if (normalizedType === 'Date') return field.isOptional ? 'An5.DateTimeNullableFilter' : 'An5.DateTimeFilter';
203
- return 'any';
204
- }
205
-
206
- private normalizeType(type: string): string {
207
- const lowerType = type.toLowerCase();
208
- if (['string', 'varchar', 'nvarchar', 'char', 'nchar', 'text', 'ntext', 'xml', 'guid', 'uuid', 'uniqueidentifier', 'sysname', 'hierarchyid', 'geography', 'geometry', 'sql_variant', 'sqlvariant', 'rowversion', 'variant'].includes(lowerType)) {
209
- return 'string';
210
- }
211
- if (['int', 'integer', 'smallint', 'tinyint', 'bigint', 'long', 'number', 'decimal', 'numeric', 'money', 'smallmoney', 'float', 'real', 'double'].includes(lowerType)) {
212
- return 'number';
213
- }
214
- if (['bool', 'boolean', 'bit'].includes(lowerType)) {
215
- return 'boolean';
216
- }
217
- if (['datetime', 'datetime2', 'datetimeoffset', 'smalldatetime', 'date', 'time', 'timestamp'].includes(lowerType)) {
218
- return 'Date';
219
- }
220
- if (['bytes', 'binary', 'varbinary', 'image'].includes(lowerType)) {
221
- return 'string';
222
- }
223
- return 'any';
224
- }
225
-
226
- private toCamelCase(str: string): string {
227
- if (!str) return '';
228
- return str[0].toLowerCase() + str.slice(1);
229
- }
230
- }
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { Model, Field, Relation } from './types';
4
+
5
+ export class CodeGenerator {
6
+ constructor(private outputDir: string) {
7
+ if (!fs.existsSync(this.outputDir)) {
8
+ fs.mkdirSync(this.outputDir, { recursive: true });
9
+ }
10
+ }
11
+
12
+ public generate(models: Model[]) {
13
+ this.generateBaseTs();
14
+ for (const model of models) {
15
+ this.generateModelFile(model);
16
+ }
17
+ this.generateIndexTs(models);
18
+ }
19
+
20
+ private generateBaseTs() {
21
+ const baseTs = `
22
+ import { An5ClientKnownRequestError as An5KnownError } from "@an5/orm";
23
+
24
+ export namespace An5 {
25
+ export const An5ClientKnownRequestError: typeof An5KnownError = An5KnownError;
26
+ export type An5ClientKnownRequestError = An5KnownError;
27
+ export type SortOrder = 'asc' | 'desc';
28
+ export type StringFilter = { equals?: string; in?: string[]; notIn?: string[]; lt?: string; lte?: string; gt?: string; gte?: string; contains?: string; startsWith?: string; endsWith?: string; not?: string | StringFilter; };
29
+ export type StringNullableFilter = { equals?: string | null; in?: (string | null)[]; notIn?: (string | null)[]; lt?: string; lte?: string; gt?: string; gte?: string; contains?: string; startsWith?: string; endsWith?: string; not?: string | StringNullableFilter | null; };
30
+ export type NumberFilter = { equals?: number; in?: number[]; notIn?: number[]; lt?: number; lte?: number; gt?: number; gte?: number; not?: number | NumberFilter; };
31
+ export type IntFieldUpdateOperationsInput = { set?: number; increment?: number; decrement?: number; multiply?: number; divide?: number; };
32
+ export type FloatFieldUpdateOperationsInput = { set?: number; increment?: number; decrement?: number; multiply?: number; divide?: number; };
33
+ export type NumberNullableFilter = { equals?: number | null; in?: (number | null)[]; notIn?: (number | null)[]; lt?: number; lte?: number; gt?: number; gte?: number; not?: number | NumberNullableFilter | null; };
34
+ export type BooleanFilter = { equals?: boolean; not?: boolean | BooleanFilter; };
35
+ export type BooleanNullableFilter = { equals?: boolean | null; not?: boolean | BooleanNullableFilter | null; };
36
+ export type DateTimeFilter = { equals?: Date | string; in?: (Date | string)[]; notIn?: (Date | string)[]; lt?: Date | string; lte?: Date | string; gt?: Date | string; gte?: Date | string; not?: Date | string | DateTimeFilter; };
37
+ export type DateTimeNullableFilter = { equals?: Date | string | null; in?: ((Date | string) | null)[]; notIn?: ((Date | string) | null)[]; lt?: Date | string; lte?: Date | string; gt?: Date | string; gte?: Date | string; not?: Date | string | DateTimeNullableFilter | null; };
38
+ }
39
+
40
+ export interface TableClient<T, WhereInput = any, _Select = any, Include = any, CreateInput = any, UpdateInput = any, FindManyArgs = any, FindFirstArgs = any, FindUniqueArgs = any, CreateArgs = any, UpdateArgs = any, UpsertArgs = any, DeleteArgs = any, AggregateArgs = any, GroupByArgs = any> {
41
+ findMany(args?: FindManyArgs): Promise<T[]>;
42
+ vectorSearch(args: { vector: number[]; take?: number; where?: WhereInput; include?: Include; vectorField?: string; distanceMetric?: 'cosine' | 'euclidean' | 'dot'; }): Promise<(T & { distance: number })[]>;
43
+ findFirst(args?: FindFirstArgs): Promise<T | null>;
44
+ findUnique(args?: FindUniqueArgs): Promise<T | null>;
45
+ count(args?: { where?: WhereInput; }): Promise<number>;
46
+ create(args: CreateArgs): Promise<T>;
47
+ createMany(args: { data: CreateInput[]; skipDuplicates?: boolean; }): Promise<{ count: number }>;
48
+ update(args: UpdateArgs): Promise<T>;
49
+ updateMany(args: { where?: WhereInput; data: UpdateInput; }): Promise<{ count: number }>;
50
+ delete(args: DeleteArgs): Promise<T>;
51
+ deleteMany(args?: { where?: WhereInput; }): Promise<{ count: number }>;
52
+ aggregate(args: AggregateArgs): Promise<any>;
53
+ groupBy(args: GroupByArgs): Promise<any[]>;
54
+ upsert(args: UpsertArgs): Promise<T>;
55
+ }
56
+ `;
57
+ fs.writeFileSync(path.join(this.outputDir, 'base.ts'), baseTs);
58
+ }
59
+
60
+ private generateModelFile(model: Model) {
61
+ let content = `import { An5, TableClient } from './base';\n`;
62
+ const relModels = [...new Set(model.relations.map(r => r.type))];
63
+ for (const relModel of relModels) {
64
+ if (relModel !== model.name) {
65
+ content += `import { ${relModel}, ${relModel}WhereInput, ${relModel}FindManyArgs, ${relModel}CreateInput, ${relModel}UpdateInput } from './${relModel}';\n`;
66
+ }
67
+ }
68
+
69
+ content += `\nexport interface ${model.name} {\n`;
70
+ for (const field of model.fields) {
71
+ content += ` ${field.name}: ${this.normalizeType(field.type)}${field.isOptional ? ' | null' : ''};\n`;
72
+ }
73
+ for (const rel of model.relations) {
74
+ content += ` ${rel.name}?: ${rel.type}${rel.isArray ? '[]' : ''}${rel.isOptional ? ' | null' : ''};\n`;
75
+ }
76
+ if (model.relations.length > 0) {
77
+ content += ` _count?: { ${model.relations.map(r => `${r.name}: number`).join('; ')} };\n`;
78
+ }
79
+ content += `}\n\n`;
80
+
81
+ content += `export type ${model.name}WhereInput = {\n`;
82
+ content += ` AND?: ${model.name}WhereInput | ${model.name}WhereInput[];\n`;
83
+ content += ` OR?: ${model.name}WhereInput[];\n`;
84
+ content += ` NOT?: ${model.name}WhereInput | ${model.name}WhereInput[];\n`;
85
+ for (const field of model.fields) {
86
+ const filter = this.getFieldFilterType(field);
87
+ content += ` ${field.name}?: ${this.inputType(field.type)} | ${filter}${field.isOptional ? ' | null' : ''};\n`;
88
+ }
89
+ for (const rel of model.relations) {
90
+ if (rel.isArray) {
91
+ content += ` ${rel.name}?: { some?: ${rel.type}WhereInput; none?: ${rel.type}WhereInput; every?: ${rel.type}WhereInput; };\n`;
92
+ } else {
93
+ content += ` ${rel.name}?: ${rel.type}WhereInput${rel.isOptional ? ' | null' : ''};\n`;
94
+ }
95
+ }
96
+ if (model.compoundUniques) {
97
+ for (const compound of model.compoundUniques) {
98
+ const keyName = compound.join('_');
99
+ content += ` ${keyName}?: { ${compound.map(f => `${f}: ${model.fields.find(mf => mf.name === f)?.type || 'any'}`).join('; ')} };\n`;
100
+ }
101
+ }
102
+ content += `};\n\n`;
103
+
104
+ const selectFields = model.fields.map(f => `${f.name}?: boolean`);
105
+ const selectRels = model.relations.map(r => `${r.name}?: boolean | ${r.type}FindManyArgs`);
106
+ if (model.relations.length > 0) {
107
+ selectRels.push(`_count?: boolean | { select?: { ${model.relations.map(r => `${r.name}?: boolean`).join('; ')} } }`);
108
+ }
109
+ content += `export type ${model.name}Select = { ${[...selectFields, ...selectRels].join('; ')}${[...selectFields, ...selectRels].length > 0 ? ';' : ''} };\n`;
110
+
111
+ const includeRels = model.relations.map(r => `${r.name}?: boolean | ${r.type}FindManyArgs`);
112
+ if (model.relations.length > 0) {
113
+ includeRels.push(`_count?: boolean | { select?: { ${model.relations.map(r => `${r.name}?: boolean`).join('; ')} } }`);
114
+ }
115
+ content += `export type ${model.name}Include = { ${includeRels.join('; ')}${includeRels.length > 0 ? ';' : ''} };\n`;
116
+
117
+ content += `export type ${model.name}CreateInput = { ${model.fields.map(f => `${f.name}${f.isOptional || f.hasDefault ? '?' : ''}: ${this.inputType(f.type)}${f.isOptional ? ' | null' : ''}`).join('; ')}; ${model.relations.map(r => `${r.name}?: { create?: ${r.type}CreateInput | ${r.type}CreateInput[]; connect?: ${r.type}WhereInput | ${r.type}WhereInput[]; }`).join('; ')} };\n`;
118
+ content += `export type ${model.name}UpdateInput = { ${model.fields.filter(f => !f.isId).map(f => `${f.name}?: ${f.type === 'number' ? 'number | An5.IntFieldUpdateOperationsInput' : this.inputType(f.type)}${f.isOptional ? ' | null' : ''}`).join('; ')}; ${model.relations.map(r => `${r.name}?: { create?: ${r.type}CreateInput | ${r.type}CreateInput[]; connect?: ${r.type}WhereInput | ${r.type}WhereInput[]; set?: ${r.type}WhereInput | ${r.type}WhereInput[]; disconnect?: ${r.type}WhereInput | ${r.type}WhereInput[]; delete?: ${r.type}WhereInput | ${r.type}WhereInput[]; update?: { where: ${r.type}WhereInput; data: ${r.type}UpdateInput; } | { where: ${r.type}WhereInput; data: ${r.type}UpdateInput; }[]; upsert?: { where: ${r.type}WhereInput; create: ${r.type}CreateInput; update: ${r.type}UpdateInput; } | { where: ${r.type}WhereInput; create: ${r.type}CreateInput; update: ${r.type}UpdateInput; }[]; }`).join('; ')} };\n`;
119
+
120
+ content += `export type ${model.name}OrderByInput = { ${model.fields.map(f => `${f.name}?: An5.SortOrder`).join('; ')} };\n`;
121
+ content += `export type ${model.name}FindManyArgs = { where?: ${model.name}WhereInput; orderBy?: ${model.name}OrderByInput | ${model.name}OrderByInput[]; take?: number; skip?: number; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
122
+ content += `export type ${model.name}FindFirstArgs = { where?: ${model.name}WhereInput; orderBy?: ${model.name}OrderByInput | ${model.name}OrderByInput[]; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
123
+ content += `export type ${model.name}FindUniqueArgs = { where?: ${model.name}WhereInput; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
124
+ content += `export type ${model.name}CreateArgs = { data: ${model.name}CreateInput; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
125
+ content += `export type ${model.name}UpdateArgs = { where: ${model.name}WhereInput; data: ${model.name}UpdateInput; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
126
+ content += `export type ${model.name}UpsertArgs = { where: ${model.name}WhereInput; create: ${model.name}CreateInput; update: ${model.name}UpdateInput; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
127
+ content += `export type ${model.name}DeleteArgs = { where: ${model.name}WhereInput; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
128
+ content += `export type ${model.name}ScalarFieldEnum = ${model.fields.map(f => `'${f.name}'`).join(' | ') || 'never'};\n`;
129
+ content += `export type ${model.name}AggregateArgs = { where?: ${model.name}WhereInput; _count?: true | { _all?: true; ${model.fields.map(f => `${f.name}?: true`).join('; ')} }; _sum?: { ${model.fields.filter(f => this.normalizeType(f.type) === 'number').map(f => `${f.name}?: true`).join('; ')} }; _avg?: { ${model.fields.filter(f => this.normalizeType(f.type) === 'number').map(f => `${f.name}?: true`).join('; ')} }; _min?: { ${model.fields.map(f => `${f.name}?: true`).join('; ')} }; _max?: { ${model.fields.map(f => `${f.name}?: true`).join('; ')} }; };\n`;
130
+ const numericHavingFields = model.fields.filter(f => this.normalizeType(f.type) === 'number').map(f => `${f.name}?: An5.NumberFilter | number`).join('; ');
131
+ const scalarHavingFields = model.fields.map(f => `${f.name}?: any`).join('; ');
132
+ content += `export type ${model.name}AggregateHavingInput = { _count?: { _all?: An5.NumberFilter | number; ${model.fields.map(f => `${f.name}?: An5.NumberFilter | number`).join('; ')} }; _sum?: { ${numericHavingFields} }; _avg?: { ${numericHavingFields} }; _min?: { ${scalarHavingFields} }; _max?: { ${scalarHavingFields} }; };\n`;
133
+ content += `export type ${model.name}GroupByArgs = { by: ${model.name}ScalarFieldEnum | ${model.name}ScalarFieldEnum[]; where?: ${model.name}WhereInput; having?: ${model.name}AggregateHavingInput; orderBy?: ${model.name}OrderByInput | ${model.name}OrderByInput[]; skip?: number; take?: number; _count?: true | { _all?: true; ${model.fields.map(f => `${f.name}?: true`).join('; ')} }; _sum?: { ${model.fields.filter(f => this.normalizeType(f.type) === 'number').map(f => `${f.name}?: true`).join('; ')} }; _avg?: { ${model.fields.filter(f => this.normalizeType(f.type) === 'number').map(f => `${f.name}?: true`).join('; ')} }; _min?: { ${model.fields.map(f => `${f.name}?: true`).join('; ')} }; _max?: { ${model.fields.map(f => `${f.name}?: true`).join('; ')} }; };\n`;
134
+
135
+ content += `export type ${model.name}TableClient = TableClient<\n ${model.name},\n ${model.name}WhereInput,\n ${model.name}Select,\n ${model.name}Include,\n ${model.name}CreateInput,\n ${model.name}UpdateInput,\n ${model.name}FindManyArgs,\n ${model.name}FindFirstArgs,\n ${model.name}FindUniqueArgs,\n ${model.name}CreateArgs,\n ${model.name}UpdateArgs,\n ${model.name}UpsertArgs,\n ${model.name}DeleteArgs,\n ${model.name}AggregateArgs,\n ${model.name}GroupByArgs\n>;\n`;
136
+
137
+ fs.writeFileSync(path.join(this.outputDir, `${model.name}.ts`), content);
138
+ }
139
+
140
+ private generateIndexTs(models: Model[]) {
141
+ let content = `export * from './base';\n`;
142
+ for (const model of models) {
143
+ content += `export * from './${model.name}';\n`;
144
+ }
145
+ content += `\nimport { An5 as BaseAn5 } from './base';\n`;
146
+ for (const model of models) {
147
+ content += `import * as ${model.name}Types from './${model.name}';\n`;
148
+ }
149
+
150
+ content += `\nexport namespace An5 {\n`;
151
+ content += ` export type SortOrder = BaseAn5.SortOrder;\n`;
152
+ content += ` export type StringFilter = BaseAn5.StringFilter;\n`;
153
+ content += ` export type StringNullableFilter = BaseAn5.StringNullableFilter;\n`;
154
+ content += ` export type NumberFilter = BaseAn5.NumberFilter;\n`;
155
+ content += ` export type NumberNullableFilter = BaseAn5.NumberNullableFilter;\n`;
156
+ content += ` export type BooleanFilter = BaseAn5.BooleanFilter;\n`;
157
+ content += ` export type BooleanNullableFilter = BaseAn5.BooleanNullableFilter;\n`;
158
+ content += ` export type DateTimeFilter = BaseAn5.DateTimeFilter;\n`;
159
+ content += ` export type DateTimeNullableFilter = BaseAn5.DateTimeNullableFilter;\n`;
160
+ content += ` export type IntFieldUpdateOperationsInput = BaseAn5.IntFieldUpdateOperationsInput;\n`;
161
+ content += ` export type FloatFieldUpdateOperationsInput = BaseAn5.FloatFieldUpdateOperationsInput;\n`;
162
+ content += ` export const An5ClientKnownRequestError = BaseAn5.An5ClientKnownRequestError;\n`;
163
+
164
+ for (const model of models) {
165
+ content += ` export type ${model.name} = ${model.name}Types.${model.name};\n`;
166
+ content += ` export type ${model.name}WhereInput = ${model.name}Types.${model.name}WhereInput;\n`;
167
+ content += ` export type ${model.name}Select = ${model.name}Types.${model.name}Select;\n`;
168
+ content += ` export type ${model.name}Include = ${model.name}Types.${model.name}Include;\n`;
169
+ content += ` export type ${model.name}CreateInput = ${model.name}Types.${model.name}CreateInput;\n`;
170
+ content += ` export type ${model.name}UpdateInput = ${model.name}Types.${model.name}UpdateInput;\n`;
171
+ content += ` export type ${model.name}OrderByInput = ${model.name}Types.${model.name}OrderByInput;\n`;
172
+ content += ` export type ${model.name}FindManyArgs = ${model.name}Types.${model.name}FindManyArgs;\n`;
173
+ content += ` export type ${model.name}FindFirstArgs = ${model.name}Types.${model.name}FindFirstArgs;\n`;
174
+ content += ` export type ${model.name}FindUniqueArgs = ${model.name}Types.${model.name}FindUniqueArgs;\n`;
175
+ content += ` export type ${model.name}CreateArgs = ${model.name}Types.${model.name}CreateArgs;\n`;
176
+ content += ` export type ${model.name}UpdateArgs = ${model.name}Types.${model.name}UpdateArgs;\n`;
177
+ content += ` export type ${model.name}UpsertArgs = ${model.name}Types.${model.name}UpsertArgs;\n`;
178
+ content += ` export type ${model.name}DeleteArgs = ${model.name}Types.${model.name}DeleteArgs;\n`;
179
+ content += ` export type ${model.name}TableClient = ${model.name}Types.${model.name}TableClient;\n`;
180
+ content += ` export type ${model.name}ScalarFieldEnum = ${model.name}Types.${model.name}ScalarFieldEnum;\n`;
181
+ content += ` export type ${model.name}AggregateArgs = ${model.name}Types.${model.name}AggregateArgs;\n`;
182
+ content += ` export type ${model.name}GroupByArgs = ${model.name}Types.${model.name}GroupByArgs;\n`;
183
+ }
184
+ content += `}\n`;
185
+
186
+ content += `\nexport class An5Client {\n $connect(): Promise<void> { return Promise.resolve(); }\n $disconnect(): Promise<void> { return Promise.resolve(); }\n $transaction<R>(fn: (tx: An5Client) => Promise<R>, options?: { timeout?: number }): Promise<R>;\n $transaction<R>(list: Promise<R>[]): Promise<R[]>;\n $transaction(fn: any, _options?: any): Promise<any> { return typeof fn === 'function' ? fn(this) : Promise.all(fn); }\n $begin(): Promise<An5Client> { return Promise.resolve(this); }\n $commit(): Promise<void> { return Promise.resolve(); }\n $rollback(): Promise<void> { return Promise.resolve(); }\n $queryRaw<T = any>(_queryParts: TemplateStringsArray | string, ..._values: any[]): Promise<T> { return Promise.resolve([] as any); }\n $queryRawUnsafe<R = any>(_query: string, ..._values: any[]): Promise<R> { return Promise.resolve([] as any); }\n $executeRaw<_T = any>(_queryParts: TemplateStringsArray | string, ..._values: any[]): Promise<any> { return Promise.resolve(0 as any); }\n $executeRawUnsafe(_query: string, ..._values: any[]): Promise<number> { return Promise.resolve(0); }\n`;
187
+
188
+ const addedProps = new Set<string>();
189
+ for (const model of models) {
190
+ const props = this.getAllPropertyVariations(model.name);
191
+ for (const prop of props) {
192
+ if (!addedProps.has(prop)) {
193
+ content += ` ${prop}!: ${model.name}Types.${model.name}TableClient;\n`;
194
+ addedProps.add(prop);
195
+ }
196
+ }
197
+ }
198
+
199
+ content += `}\n`;
200
+ fs.writeFileSync(path.join(this.outputDir, 'index.ts'), content);
201
+ }
202
+
203
+ private getAllPropertyVariations(modelName: string): string[] {
204
+ const variations = new Set<string>();
205
+ variations.add(modelName);
206
+ variations.add(this.toCamelCase(modelName));
207
+ variations.add(modelName + 's');
208
+ variations.add(this.toCamelCase(modelName) + 's');
209
+ const acronyms = ['LLM', 'AI', 'MCP', 'IT', 'QC', 'HR', 'MR', 'WH', 'SSIS', 'API', 'URL', 'ID', 'JSON'];
210
+ for (const acronym of acronyms) {
211
+ if (modelName.startsWith(acronym)) {
212
+ variations.add(acronym.toLowerCase() + modelName.slice(acronym.length));
213
+ variations.add(acronym[0].toLowerCase() + acronym.slice(1) + modelName.slice(acronym.length));
214
+ variations.add(acronym.toLowerCase() + modelName.slice(acronym.length) + 's');
215
+ }
216
+ }
217
+ return Array.from(variations);
218
+ }
219
+
220
+ private getFieldFilterType(field: Field): string {
221
+ const normalizedType = this.normalizeType(field.type);
222
+ if (normalizedType === 'string') return field.isOptional ? 'An5.StringNullableFilter' : 'An5.StringFilter';
223
+ if (normalizedType === 'number') return field.isOptional ? 'An5.NumberNullableFilter' : 'An5.NumberFilter';
224
+ if (normalizedType === 'boolean') return field.isOptional ? 'An5.BooleanNullableFilter' : 'An5.BooleanFilter';
225
+ if (normalizedType === 'Date') return field.isOptional ? 'An5.DateTimeNullableFilter' : 'An5.DateTimeFilter';
226
+ return 'any';
227
+ }
228
+
229
+ // Input positions (create/update/where scalars) accept ISO strings as well
230
+ // as Date objects — Prisma parity for DateTime fields.
231
+ private inputType(tsType: string): string {
232
+ return tsType === 'Date' ? 'Date | string' : tsType;
233
+ }
234
+
235
+ private normalizeType(type: string): string {
236
+ const lowerType = type.toLowerCase();
237
+ if (['string', 'varchar', 'nvarchar', 'char', 'nchar', 'text', 'ntext', 'xml', 'guid', 'uuid', 'uniqueidentifier', 'sysname', 'hierarchyid', 'geography', 'geometry', 'sql_variant', 'sqlvariant', 'rowversion', 'variant'].includes(lowerType)) {
238
+ return 'string';
239
+ }
240
+ if (['int', 'integer', 'smallint', 'tinyint', 'bigint', 'long', 'number', 'decimal', 'numeric', 'money', 'smallmoney', 'float', 'real', 'double'].includes(lowerType)) {
241
+ return 'number';
242
+ }
243
+ if (['bool', 'boolean', 'bit'].includes(lowerType)) {
244
+ return 'boolean';
245
+ }
246
+ if (['datetime', 'datetime2', 'datetimeoffset', 'smalldatetime', 'date', 'time', 'timestamp'].includes(lowerType)) {
247
+ return 'Date';
248
+ }
249
+ if (['bytes', 'binary', 'varbinary', 'image'].includes(lowerType)) {
250
+ return 'string';
251
+ }
252
+ return 'any';
253
+ }
254
+
255
+ private toCamelCase(str: string): string {
256
+ if (!str) return '';
257
+ return str[0].toLowerCase() + str.slice(1);
258
+ }
259
+ }