@an5/orm 1.0.7 → 1.0.8

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 (52) hide show
  1. package/README.md +175 -153
  2. package/dist/an5Metadata.d.ts +10 -0
  3. package/dist/an5Metadata.js +29 -0
  4. package/dist/an5Orm.d.ts +4 -1
  5. package/dist/an5Orm.js +214 -409
  6. package/dist/config.d.ts +2 -0
  7. package/dist/config.js +24 -0
  8. package/dist/errors.d.ts +14 -0
  9. package/dist/errors.js +16 -0
  10. package/dist/generator/src/code-generator.d.ts +13 -13
  11. package/dist/generator/src/code-generator.js +225 -225
  12. package/dist/generator/src/dotnet-generator.d.ts +11 -11
  13. package/dist/generator/src/dotnet-generator.js +546 -546
  14. package/dist/generator/src/index.d.ts +1 -1
  15. package/dist/generator/src/index.js +88 -88
  16. package/dist/generator/src/metadata-generator.d.ts +8 -8
  17. package/dist/generator/src/metadata-generator.js +90 -90
  18. package/dist/generator/src/parser.d.ts +11 -11
  19. package/dist/generator/src/parser.js +206 -206
  20. package/dist/generator/src/python-generator.d.ts +9 -9
  21. package/dist/generator/src/python-generator.js +105 -105
  22. package/dist/generator/src/types.d.ts +27 -27
  23. package/dist/generator/src/types.js +2 -2
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.js +5 -1
  26. package/dist/logger.d.ts +53 -0
  27. package/dist/logger.js +117 -0
  28. package/dist/metadata.d.ts +24 -0
  29. package/dist/metadata.js +15 -0
  30. package/dist/scripts/release-cli.d.ts +2 -2
  31. package/dist/scripts/release-cli.js +115 -115
  32. package/dist/sql-utils.d.ts +29 -0
  33. package/dist/sql-utils.js +220 -0
  34. package/generator/src/code-generator.ts +230 -234
  35. package/generator/src/dotnet-generator.ts +544 -544
  36. package/generator/src/index.ts +116 -104
  37. package/generator/src/metadata-generator.ts +101 -99
  38. package/generator/src/parser.ts +202 -202
  39. package/generator/src/python-generator.ts +114 -114
  40. package/generator/src/types.ts +29 -29
  41. package/package.json +2 -6
  42. package/dist/cleanup.d.ts +0 -2
  43. package/dist/cleanup.js +0 -120
  44. package/dist/cli.js +0 -64
  45. package/dist/migrate.d.ts +0 -11
  46. package/dist/migrate.js +0 -301
  47. package/dist/pull.d.ts +0 -2
  48. package/dist/pull.js +0 -339
  49. package/dist/push.d.ts +0 -2
  50. package/dist/push.js +0 -281
  51. package/dist/seed.d.ts +0 -2
  52. package/dist/seed.js +0 -401
@@ -1,206 +1,206 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.SchemaParser = void 0;
7
- exports.sqlTypeToTs = sqlTypeToTs;
8
- const fs_1 = __importDefault(require("fs"));
9
- const path_1 = __importDefault(require("path"));
10
- // SQL Server type → TypeScript type mapping
11
- const AN5_TO_TS = {
12
- // String types
13
- 'NVARCHAR': 'string',
14
- 'VARCHAR': 'string',
15
- 'CHAR': 'string',
16
- 'NCHAR': 'string',
17
- 'TEXT': 'string',
18
- 'NTEXT': 'string',
19
- 'XML': 'string',
20
- // Numeric types
21
- 'INT': 'number',
22
- 'SMALLINT': 'number',
23
- 'TINYINT': 'number',
24
- 'BIGINT': 'number | bigint',
25
- 'FLOAT': 'number',
26
- 'REAL': 'number',
27
- 'DECIMAL': 'number',
28
- 'NUMERIC': 'number',
29
- 'MONEY': 'number',
30
- 'SMALLMONEY': 'number',
31
- // Boolean
32
- 'BIT': 'boolean',
33
- // Date types
34
- 'DATETIME': 'Date',
35
- 'DATETIME2': 'Date',
36
- 'SMALLDATETIME': 'Date',
37
- 'DATE': 'Date',
38
- 'TIME': 'Date',
39
- 'DATETIMEOFFSET': 'Date',
40
- // Binary types
41
- 'VARBINARY': 'Buffer',
42
- 'BINARY': 'Buffer',
43
- 'IMAGE': 'Buffer',
44
- // Other
45
- 'UNIQUEIDENTIFIER': 'string',
46
- 'SQL_VARIANT': 'any',
47
- 'ROWVERSION': 'Buffer',
48
- 'HIERARCHYID': 'string',
49
- 'GEOGRAPHY': 'string',
50
- 'GEOMETRY': 'string',
51
- 'VECTOR': 'number[] | string',
52
- };
53
- // Parse base type from "NVARCHAR(255)" → "NVARCHAR"
54
- function parseSqlType(raw) {
55
- const match = raw.match(/^(\w+)(?:\((.+)\))?$/);
56
- if (!match)
57
- return { base: raw, params: '' };
58
- return { base: match[1].toUpperCase(), params: match[2] || '' };
59
- }
60
- // Map SQL Server type to TypeScript type
61
- function sqlTypeToTs(sqlType) {
62
- const { base } = parseSqlType(sqlType);
63
- return AN5_TO_TS[base] || 'any';
64
- }
65
- class SchemaParser {
66
- constructor(schemaDir) {
67
- this.schemaDir = schemaDir;
68
- this.schemaText = '';
69
- }
70
- async parse() {
71
- this.loadSchema();
72
- const lines = this.schemaText.split('\n');
73
- const models = [];
74
- let currentModel = null;
75
- for (let line of lines) {
76
- line = line.trim();
77
- if (!line || line.startsWith('//'))
78
- continue;
79
- const modelHeaderMatch = line.match(/^model\s+(\w+)\s*\{/);
80
- if (modelHeaderMatch) {
81
- const modelName = modelHeaderMatch[1];
82
- currentModel = {
83
- name: modelName,
84
- tableName: modelName.toLowerCase() + 's',
85
- schemaName: 'dbo',
86
- fields: [],
87
- relations: []
88
- };
89
- models.push(currentModel);
90
- continue;
91
- }
92
- if (line === '}') {
93
- currentModel = null;
94
- continue;
95
- }
96
- if (currentModel) {
97
- this.parseModelLine(line, currentModel);
98
- }
99
- }
100
- this.postProcessRelations(models);
101
- return models;
102
- }
103
- loadSchema() {
104
- if (fs_1.default.existsSync(this.schemaDir)) {
105
- const files = fs_1.default.readdirSync(this.schemaDir).filter(f => f.endsWith('.an5'));
106
- for (const file of files) {
107
- this.schemaText += fs_1.default.readFileSync(path_1.default.join(this.schemaDir, file), 'utf8') + '\n';
108
- }
109
- }
110
- else {
111
- throw new Error(`No schema directory found at ${this.schemaDir}`);
112
- }
113
- }
114
- parseModelLine(line, model) {
115
- if (line.startsWith('@@map')) {
116
- const mapMatch = line.match(/@@map\("(.+)"\)/);
117
- if (mapMatch)
118
- model.tableName = mapMatch[1];
119
- return;
120
- }
121
- if (line.startsWith('@@schema')) {
122
- const schemaMatch = line.match(/@@schema\("(.+)"\)/);
123
- if (schemaMatch)
124
- model.schemaName = schemaMatch[1];
125
- return;
126
- }
127
- if (line.startsWith('@@unique')) {
128
- const uniqueMatch = line.match(/@@unique\(\[([\w,\s]+)\]\)/);
129
- if (uniqueMatch) {
130
- const fields = uniqueMatch[1].split(',').map(f => f.trim());
131
- model.compoundUniques = model.compoundUniques || [];
132
- model.compoundUniques.push(fields);
133
- }
134
- return;
135
- }
136
- if (line.startsWith('@@description')) {
137
- const descMatch = line.match(/@@description\("(.+)"\)/);
138
- if (descMatch)
139
- model.description = descMatch[1];
140
- return;
141
- }
142
- if (line.startsWith('@@'))
143
- return;
144
- const parts = line.split(/\s+/);
145
- const fieldName = parts[0];
146
- let fieldType = parts[1];
147
- if (!fieldName || !fieldType)
148
- return;
149
- const isArray = fieldType.endsWith('[]');
150
- const isOptional = fieldType.endsWith('?');
151
- const cleanType = fieldType.replace('[]', '').replace('?', '');
152
- // Parse SQL Server type (e.g., "NVARCHAR(255)" → base="NVARCHAR")
153
- const { base: sqlBase } = parseSqlType(cleanType);
154
- let tsType = 'any';
155
- let isRelation = false;
156
- // Check if it's a known SQL Server type
157
- if (AN5_TO_TS[sqlBase]) {
158
- tsType = sqlTypeToTs(cleanType);
159
- }
160
- else if (cleanType[0] === cleanType[0].toUpperCase() && !cleanType.includes('(')) {
161
- // Uppercase without parens = likely a relation to another model
162
- tsType = cleanType;
163
- isRelation = true;
164
- }
165
- if (isRelation) {
166
- let foreignKey = '', localKey = '', relationName = '';
167
- const nameMatch = line.match(/@relation\("(\w+)"/);
168
- if (nameMatch)
169
- relationName = nameMatch[1];
170
- const relationMatch = line.match(/@relation\((?:.*fields:\s*\[(\w+)\],)?\s*(?:.*references:\s*\[(\w+)\],?)?.*\)/);
171
- if (relationMatch) {
172
- foreignKey = relationMatch[1] || '';
173
- localKey = relationMatch[2] || '';
174
- }
175
- model.relations.push({ name: fieldName, type: tsType, isArray, isOptional, foreignKey, localKey, relationName });
176
- }
177
- else {
178
- const hasDefault = line.includes('@default') || line.includes('@updatedAt') || line.includes('@id');
179
- const isId = line.includes('@id');
180
- let description;
181
- const descMatch = line.match(/@description\("(.+)"\)/);
182
- if (descMatch)
183
- description = descMatch[1];
184
- model.fields.push({ name: fieldName, type: tsType, sqlType: cleanType, isOptional, hasDefault, isId, description });
185
- }
186
- }
187
- postProcessRelations(models) {
188
- for (const model of models) {
189
- for (const rel of model.relations) {
190
- if (!rel.foreignKey || !rel.localKey) {
191
- const targetModel = models.find(m => m.name === rel.type);
192
- if (targetModel) {
193
- let opposite = rel.relationName ?
194
- targetModel.relations.find(r => r.type === model.name && r.relationName === rel.relationName && r.foreignKey && r.localKey) :
195
- targetModel.relations.find(r => r.type === model.name && r.foreignKey && r.localKey);
196
- if (opposite) {
197
- rel.foreignKey = opposite.foreignKey;
198
- rel.localKey = opposite.localKey;
199
- }
200
- }
201
- }
202
- }
203
- }
204
- }
205
- }
206
- exports.SchemaParser = SchemaParser;
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SchemaParser = void 0;
7
+ exports.sqlTypeToTs = sqlTypeToTs;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ // SQL Server type → TypeScript type mapping
11
+ const AN5_TO_TS = {
12
+ // String types
13
+ 'NVARCHAR': 'string',
14
+ 'VARCHAR': 'string',
15
+ 'CHAR': 'string',
16
+ 'NCHAR': 'string',
17
+ 'TEXT': 'string',
18
+ 'NTEXT': 'string',
19
+ 'XML': 'string',
20
+ // Numeric types
21
+ 'INT': 'number',
22
+ 'SMALLINT': 'number',
23
+ 'TINYINT': 'number',
24
+ 'BIGINT': 'number | bigint',
25
+ 'FLOAT': 'number',
26
+ 'REAL': 'number',
27
+ 'DECIMAL': 'number',
28
+ 'NUMERIC': 'number',
29
+ 'MONEY': 'number',
30
+ 'SMALLMONEY': 'number',
31
+ // Boolean
32
+ 'BIT': 'boolean',
33
+ // Date types
34
+ 'DATETIME': 'Date',
35
+ 'DATETIME2': 'Date',
36
+ 'SMALLDATETIME': 'Date',
37
+ 'DATE': 'Date',
38
+ 'TIME': 'Date',
39
+ 'DATETIMEOFFSET': 'Date',
40
+ // Binary types
41
+ 'VARBINARY': 'Buffer',
42
+ 'BINARY': 'Buffer',
43
+ 'IMAGE': 'Buffer',
44
+ // Other
45
+ 'UNIQUEIDENTIFIER': 'string',
46
+ 'SQL_VARIANT': 'any',
47
+ 'ROWVERSION': 'Buffer',
48
+ 'HIERARCHYID': 'string',
49
+ 'GEOGRAPHY': 'string',
50
+ 'GEOMETRY': 'string',
51
+ 'VECTOR': 'number[] | string',
52
+ };
53
+ // Parse base type from "NVARCHAR(255)" → "NVARCHAR"
54
+ function parseSqlType(raw) {
55
+ const match = raw.match(/^(\w+)(?:\((.+)\))?$/);
56
+ if (!match)
57
+ return { base: raw, params: '' };
58
+ return { base: match[1].toUpperCase(), params: match[2] || '' };
59
+ }
60
+ // Map SQL Server type to TypeScript type
61
+ function sqlTypeToTs(sqlType) {
62
+ const { base } = parseSqlType(sqlType);
63
+ return AN5_TO_TS[base] || 'any';
64
+ }
65
+ class SchemaParser {
66
+ constructor(schemaDir) {
67
+ this.schemaDir = schemaDir;
68
+ this.schemaText = '';
69
+ }
70
+ async parse() {
71
+ this.loadSchema();
72
+ const lines = this.schemaText.split('\n');
73
+ const models = [];
74
+ let currentModel = null;
75
+ for (let line of lines) {
76
+ line = line.trim();
77
+ if (!line || line.startsWith('//'))
78
+ continue;
79
+ const modelHeaderMatch = line.match(/^model\s+(\w+)\s*\{/);
80
+ if (modelHeaderMatch) {
81
+ const modelName = modelHeaderMatch[1];
82
+ currentModel = {
83
+ name: modelName,
84
+ tableName: modelName.toLowerCase() + 's',
85
+ schemaName: 'dbo',
86
+ fields: [],
87
+ relations: []
88
+ };
89
+ models.push(currentModel);
90
+ continue;
91
+ }
92
+ if (line === '}') {
93
+ currentModel = null;
94
+ continue;
95
+ }
96
+ if (currentModel) {
97
+ this.parseModelLine(line, currentModel);
98
+ }
99
+ }
100
+ this.postProcessRelations(models);
101
+ return models;
102
+ }
103
+ loadSchema() {
104
+ if (fs_1.default.existsSync(this.schemaDir)) {
105
+ const files = fs_1.default.readdirSync(this.schemaDir).filter(f => f.endsWith('.an5'));
106
+ for (const file of files) {
107
+ this.schemaText += fs_1.default.readFileSync(path_1.default.join(this.schemaDir, file), 'utf8') + '\n';
108
+ }
109
+ }
110
+ else {
111
+ throw new Error(`No schema directory found at ${this.schemaDir}`);
112
+ }
113
+ }
114
+ parseModelLine(line, model) {
115
+ if (line.startsWith('@@map')) {
116
+ const mapMatch = line.match(/@@map\("(.+)"\)/);
117
+ if (mapMatch)
118
+ model.tableName = mapMatch[1];
119
+ return;
120
+ }
121
+ if (line.startsWith('@@schema')) {
122
+ const schemaMatch = line.match(/@@schema\("(.+)"\)/);
123
+ if (schemaMatch)
124
+ model.schemaName = schemaMatch[1];
125
+ return;
126
+ }
127
+ if (line.startsWith('@@unique')) {
128
+ const uniqueMatch = line.match(/@@unique\(\[([\w,\s]+)\]\)/);
129
+ if (uniqueMatch) {
130
+ const fields = uniqueMatch[1].split(',').map(f => f.trim());
131
+ model.compoundUniques = model.compoundUniques || [];
132
+ model.compoundUniques.push(fields);
133
+ }
134
+ return;
135
+ }
136
+ if (line.startsWith('@@description')) {
137
+ const descMatch = line.match(/@@description\("(.+)"\)/);
138
+ if (descMatch)
139
+ model.description = descMatch[1];
140
+ return;
141
+ }
142
+ if (line.startsWith('@@'))
143
+ return;
144
+ const parts = line.split(/\s+/);
145
+ const fieldName = parts[0];
146
+ let fieldType = parts[1];
147
+ if (!fieldName || !fieldType)
148
+ return;
149
+ const isArray = fieldType.endsWith('[]');
150
+ const isOptional = fieldType.endsWith('?');
151
+ const cleanType = fieldType.replace('[]', '').replace('?', '');
152
+ // Parse SQL Server type (e.g., "NVARCHAR(255)" → base="NVARCHAR")
153
+ const { base: sqlBase } = parseSqlType(cleanType);
154
+ let tsType = 'any';
155
+ let isRelation = false;
156
+ // Check if it's a known SQL Server type
157
+ if (AN5_TO_TS[sqlBase]) {
158
+ tsType = sqlTypeToTs(cleanType);
159
+ }
160
+ else if (cleanType[0] === cleanType[0].toUpperCase() && !cleanType.includes('(')) {
161
+ // Uppercase without parens = likely a relation to another model
162
+ tsType = cleanType;
163
+ isRelation = true;
164
+ }
165
+ if (isRelation) {
166
+ let foreignKey = '', localKey = '', relationName = '';
167
+ const nameMatch = line.match(/@relation\("(\w+)"/);
168
+ if (nameMatch)
169
+ relationName = nameMatch[1];
170
+ const relationMatch = line.match(/@relation\((?:.*fields:\s*\[(\w+)\],)?\s*(?:.*references:\s*\[(\w+)\],?)?.*\)/);
171
+ if (relationMatch) {
172
+ foreignKey = relationMatch[1] || '';
173
+ localKey = relationMatch[2] || '';
174
+ }
175
+ model.relations.push({ name: fieldName, type: tsType, isArray, isOptional, foreignKey, localKey, relationName });
176
+ }
177
+ else {
178
+ const hasDefault = line.includes('@default') || line.includes('@updatedAt') || line.includes('@id');
179
+ const isId = line.includes('@id');
180
+ let description;
181
+ const descMatch = line.match(/@description\("(.+)"\)/);
182
+ if (descMatch)
183
+ description = descMatch[1];
184
+ model.fields.push({ name: fieldName, type: tsType, sqlType: cleanType, isOptional, hasDefault, isId, description });
185
+ }
186
+ }
187
+ postProcessRelations(models) {
188
+ for (const model of models) {
189
+ for (const rel of model.relations) {
190
+ if (!rel.foreignKey || !rel.localKey) {
191
+ const targetModel = models.find(m => m.name === rel.type);
192
+ if (targetModel) {
193
+ let opposite = rel.relationName ?
194
+ targetModel.relations.find(r => r.type === model.name && r.relationName === rel.relationName && r.foreignKey && r.localKey) :
195
+ targetModel.relations.find(r => r.type === model.name && r.foreignKey && r.localKey);
196
+ if (opposite) {
197
+ rel.foreignKey = opposite.foreignKey;
198
+ rel.localKey = opposite.localKey;
199
+ }
200
+ }
201
+ }
202
+ }
203
+ }
204
+ }
205
+ }
206
+ exports.SchemaParser = SchemaParser;
@@ -1,9 +1,9 @@
1
- import { Model } from './types';
2
- export declare class PythonGenerator {
3
- private outputPath;
4
- constructor(outputPath: string);
5
- generate(models: Model[]): void;
6
- private getAllPropertyVariations;
7
- private toCamelCase;
8
- private toSnakeCase;
9
- }
1
+ import { Model } from './types';
2
+ export declare class PythonGenerator {
3
+ private outputPath;
4
+ constructor(outputPath: string);
5
+ generate(models: Model[]): void;
6
+ private getAllPropertyVariations;
7
+ private toCamelCase;
8
+ private toSnakeCase;
9
+ }
@@ -1,105 +1,105 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.PythonGenerator = void 0;
7
- const fs_1 = __importDefault(require("fs"));
8
- class PythonGenerator {
9
- constructor(outputPath) {
10
- this.outputPath = outputPath;
11
- }
12
- generate(models) {
13
- let pyContent = '# This file is auto-generated. Do not edit directly.\n\n';
14
- // 1. MODEL_TO_TABLE
15
- pyContent += 'MODEL_TO_TABLE = {\n';
16
- for (const model of models) {
17
- const props = this.getAllPropertyVariations(model.name);
18
- const fullTableName = `[${model.schemaName}].[${model.tableName}]`;
19
- for (const prop of props) {
20
- pyContent += ` "${prop}": "${fullTableName}",\n`;
21
- }
22
- }
23
- pyContent += '}\n\n';
24
- // 2. MODEL_DESCRIPTIONS
25
- pyContent += 'MODEL_DESCRIPTIONS = {\n';
26
- for (const model of models) {
27
- const props = this.getAllPropertyVariations(model.name);
28
- for (const prop of props) {
29
- pyContent += ` "${prop}": ${this.pyString(model.description)},\n`;
30
- }
31
- }
32
- pyContent += '}\n\n';
33
- // 3. MODEL_FIELDS
34
- pyContent += 'MODEL_FIELDS = {\n';
35
- for (const model of models) {
36
- const props = this.getAllPropertyVariations(model.name);
37
- const fieldsStr = this.formatFields(model);
38
- for (const prop of props) {
39
- pyContent += ` "${prop}": ${fieldsStr},\n`;
40
- }
41
- }
42
- pyContent += '}\n\n';
43
- // 4. RELATION_MAP
44
- pyContent += 'RELATION_MAP = {\n';
45
- for (const model of models) {
46
- const props = this.getAllPropertyVariations(model.name);
47
- let relationsContent = `{\n`;
48
- for (const rel of model.relations) {
49
- relationsContent += ` "${rel.name}": {\n`;
50
- relationsContent += ` "modelName": "${this.toCamelCase(rel.type)}",\n`;
51
- relationsContent += ` "relationType": "${rel.isArray ? 'many' : 'one'}",\n`;
52
- relationsContent += ` "foreignKey": "${rel.foreignKey || 'id'}",\n`;
53
- relationsContent += ` "localKey": "${rel.localKey || 'id'}"\n`;
54
- relationsContent += ` },\n`;
55
- }
56
- relationsContent += ` }`;
57
- for (const prop of props) {
58
- pyContent += ` "${prop}": ${relationsContent},\n`;
59
- }
60
- }
61
- pyContent += '}\n';
62
- fs_1.default.writeFileSync(this.outputPath, pyContent);
63
- }
64
- getAllPropertyVariations(modelName) {
65
- const variations = new Set();
66
- // 1. camelCase (User -> user, McpServer -> mcpServer)
67
- variations.add(this.toCamelCase(modelName));
68
- // 2. snake_case (User -> user, McpServer -> mcp_server)
69
- variations.add(this.toSnakeCase(modelName));
70
- const acronyms = ['LLM', 'AI', 'MCP', 'IT', 'QC', 'HR', 'MR', 'WH', 'SSIS', 'API', 'URL', 'ID', 'JSON'];
71
- for (const acronym of acronyms) {
72
- if (modelName.startsWith(acronym)) {
73
- variations.add(acronym.toLowerCase() + modelName.slice(acronym.length));
74
- variations.add(acronym[0].toLowerCase() + acronym.slice(1) + modelName.slice(acronym.length));
75
- variations.add(this.toSnakeCase(acronym.toLowerCase() + modelName.slice(acronym.length)));
76
- }
77
- }
78
- return Array.from(variations);
79
- }
80
- formatFields(model) {
81
- if (model.fields.length === 0)
82
- return '[]';
83
- const fields = model.fields.map(f => {
84
- return ` { "name": "${f.name}", "type": "${f.type}${f.isOptional ? '?' : ''}", "sql": "${f.sqlType}", "isOptional": ${f.isOptional ? 'True' : 'False'}, "hasDefault": ${f.hasDefault ? 'True' : 'False'}, "isId": ${f.isId ? 'True' : 'False'}, "description": ${this.pyString(f.description)} }`;
85
- });
86
- return `[\n${fields.join(',\n')}\n ]`;
87
- }
88
- pyString(value) {
89
- return value ? JSON.stringify(value) : 'None';
90
- }
91
- toCamelCase(str) {
92
- if (!str)
93
- return '';
94
- return str[0].toLowerCase() + str.slice(1);
95
- }
96
- toSnakeCase(str) {
97
- if (!str)
98
- return '';
99
- return str
100
- .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
101
- .replace(/([a-z\d])([A-Z])/g, '$1_$2')
102
- .toLowerCase();
103
- }
104
- }
105
- exports.PythonGenerator = PythonGenerator;
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PythonGenerator = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ class PythonGenerator {
9
+ constructor(outputPath) {
10
+ this.outputPath = outputPath;
11
+ }
12
+ generate(models) {
13
+ let pyContent = '# This file is auto-generated. Do not edit directly.\n\n';
14
+ // 1. MODEL_TO_TABLE
15
+ pyContent += 'MODEL_TO_TABLE = {\n';
16
+ for (const model of models) {
17
+ const props = this.getAllPropertyVariations(model.name);
18
+ const fullTableName = `[${model.schemaName}].[${model.tableName}]`;
19
+ for (const prop of props) {
20
+ pyContent += ` "${prop}": "${fullTableName}",\n`;
21
+ }
22
+ }
23
+ pyContent += '}\n\n';
24
+ // 2. MODEL_DESCRIPTIONS
25
+ pyContent += 'MODEL_DESCRIPTIONS = {\n';
26
+ for (const model of models) {
27
+ const props = this.getAllPropertyVariations(model.name);
28
+ for (const prop of props) {
29
+ pyContent += ` "${prop}": ${this.pyString(model.description)},\n`;
30
+ }
31
+ }
32
+ pyContent += '}\n\n';
33
+ // 3. MODEL_FIELDS
34
+ pyContent += 'MODEL_FIELDS = {\n';
35
+ for (const model of models) {
36
+ const props = this.getAllPropertyVariations(model.name);
37
+ const fieldsStr = this.formatFields(model);
38
+ for (const prop of props) {
39
+ pyContent += ` "${prop}": ${fieldsStr},\n`;
40
+ }
41
+ }
42
+ pyContent += '}\n\n';
43
+ // 4. RELATION_MAP
44
+ pyContent += 'RELATION_MAP = {\n';
45
+ for (const model of models) {
46
+ const props = this.getAllPropertyVariations(model.name);
47
+ let relationsContent = `{\n`;
48
+ for (const rel of model.relations) {
49
+ relationsContent += ` "${rel.name}": {\n`;
50
+ relationsContent += ` "modelName": "${this.toCamelCase(rel.type)}",\n`;
51
+ relationsContent += ` "relationType": "${rel.isArray ? 'many' : 'one'}",\n`;
52
+ relationsContent += ` "foreignKey": "${rel.foreignKey || 'id'}",\n`;
53
+ relationsContent += ` "localKey": "${rel.localKey || 'id'}"\n`;
54
+ relationsContent += ` },\n`;
55
+ }
56
+ relationsContent += ` }`;
57
+ for (const prop of props) {
58
+ pyContent += ` "${prop}": ${relationsContent},\n`;
59
+ }
60
+ }
61
+ pyContent += '}\n';
62
+ fs_1.default.writeFileSync(this.outputPath, pyContent);
63
+ }
64
+ getAllPropertyVariations(modelName) {
65
+ const variations = new Set();
66
+ // 1. camelCase (User -> user, McpServer -> mcpServer)
67
+ variations.add(this.toCamelCase(modelName));
68
+ // 2. snake_case (User -> user, McpServer -> mcp_server)
69
+ variations.add(this.toSnakeCase(modelName));
70
+ const acronyms = ['LLM', 'AI', 'MCP', 'IT', 'QC', 'HR', 'MR', 'WH', 'SSIS', 'API', 'URL', 'ID', 'JSON'];
71
+ for (const acronym of acronyms) {
72
+ if (modelName.startsWith(acronym)) {
73
+ variations.add(acronym.toLowerCase() + modelName.slice(acronym.length));
74
+ variations.add(acronym[0].toLowerCase() + acronym.slice(1) + modelName.slice(acronym.length));
75
+ variations.add(this.toSnakeCase(acronym.toLowerCase() + modelName.slice(acronym.length)));
76
+ }
77
+ }
78
+ return Array.from(variations);
79
+ }
80
+ formatFields(model) {
81
+ if (model.fields.length === 0)
82
+ return '[]';
83
+ const fields = model.fields.map(f => {
84
+ return ` { "name": "${f.name}", "type": "${f.type}${f.isOptional ? '?' : ''}", "sql": "${f.sqlType}", "isOptional": ${f.isOptional ? 'True' : 'False'}, "hasDefault": ${f.hasDefault ? 'True' : 'False'}, "isId": ${f.isId ? 'True' : 'False'}, "description": ${this.pyString(f.description)} }`;
85
+ });
86
+ return `[\n${fields.join(',\n')}\n ]`;
87
+ }
88
+ pyString(value) {
89
+ return value ? JSON.stringify(value) : 'None';
90
+ }
91
+ toCamelCase(str) {
92
+ if (!str)
93
+ return '';
94
+ return str[0].toLowerCase() + str.slice(1);
95
+ }
96
+ toSnakeCase(str) {
97
+ if (!str)
98
+ return '';
99
+ return str
100
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
101
+ .replace(/([a-z\d])([A-Z])/g, '$1_$2')
102
+ .toLowerCase();
103
+ }
104
+ }
105
+ exports.PythonGenerator = PythonGenerator;