@an5/orm 1.0.0 → 1.0.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.
@@ -0,0 +1,72 @@
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.MetadataGenerator = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ class MetadataGenerator {
9
+ constructor(outputPath) {
10
+ this.outputPath = outputPath;
11
+ }
12
+ generate(models) {
13
+ let metaContent = '// This file is auto-generated. Do not edit directly.\n\n';
14
+ metaContent += 'export const modelToTable: Record<string, string> = {\n';
15
+ for (const model of models) {
16
+ const props = this.getAllPropertyVariations(model.name);
17
+ const fullTableName = `[${model.schemaName}].[${model.tableName}]`;
18
+ for (const prop of props) {
19
+ metaContent += ` ${prop}: "${fullTableName}",\n`;
20
+ }
21
+ }
22
+ metaContent += '};\n\n';
23
+ metaContent += 'export const modelFields: Record<string, Record<string, { ts: string; sql: string }>> = {\n';
24
+ for (const model of models) {
25
+ const props = this.getAllPropertyVariations(model.name);
26
+ const fieldsStr = `{ ${model.fields.map(f => `${f.name}: { ts: "${f.type}${f.isOptional ? '?' : ''}", sql: "${f.sqlType}" }`).join(', ')} }`;
27
+ for (const prop of props) {
28
+ metaContent += ` ${prop}: ${fieldsStr},\n`;
29
+ }
30
+ }
31
+ metaContent += '};\n\n';
32
+ metaContent += 'export interface RelationDef {\n modelName: string;\n relationType: "many" | "one";\n foreignKey: string;\n localKey: string;\n}\n\n';
33
+ metaContent += 'export const relationMap: Record<string, Record<string, RelationDef>> = {\n';
34
+ for (const model of models) {
35
+ const props = this.getAllPropertyVariations(model.name);
36
+ let relationsContent = ` {\n`;
37
+ for (const rel of model.relations) {
38
+ relationsContent += ` ${rel.name}: { modelName: "${this.toCamelCase(rel.type)}", relationType: "${rel.isArray ? 'many' : 'one'}", foreignKey: "${rel.foreignKey || 'id'}", localKey: "${rel.localKey || 'id'}" },\n`;
39
+ }
40
+ relationsContent += ` }`;
41
+ for (const prop of props) {
42
+ metaContent += ` ${prop}: ${relationsContent},\n`;
43
+ }
44
+ }
45
+ metaContent += '};\n';
46
+ fs_1.default.writeFileSync(this.outputPath, metaContent);
47
+ }
48
+ getAllPropertyVariations(modelName) {
49
+ const variations = new Set();
50
+ // 1. Standard camelCase (e.g. User -> user, McpServer -> mcpServer)
51
+ variations.add(this.toCamelCase(modelName));
52
+ // 2. Handle known acronyms at start (e.g. LLMProvider -> lLMProvider)
53
+ // This is already handled by toCamelCase if it only lowercases the first letter.
54
+ // But we might want 'llmProvider' as well.
55
+ const acronyms = ['LLM', 'AI', 'MCP', 'IT', 'QC', 'HR', 'MR', 'WH', 'SSIS', 'API', 'URL', 'ID', 'JSON'];
56
+ for (const acronym of acronyms) {
57
+ if (modelName.startsWith(acronym)) {
58
+ // e.g. LLMProvider -> llmProvider
59
+ variations.add(acronym.toLowerCase() + modelName.slice(acronym.length));
60
+ // e.g. LLMProvider -> lLMProvider (Prisma style)
61
+ variations.add(acronym[0].toLowerCase() + acronym.slice(1) + modelName.slice(acronym.length));
62
+ }
63
+ }
64
+ return Array.from(variations);
65
+ }
66
+ toCamelCase(str) {
67
+ if (!str)
68
+ return '';
69
+ return str[0].toLowerCase() + str.slice(1);
70
+ }
71
+ }
72
+ exports.MetadataGenerator = MetadataGenerator;
@@ -0,0 +1,11 @@
1
+ import { Model } from './types';
2
+ export declare function sqlTypeToTs(sqlType: string): string;
3
+ export declare class SchemaParser {
4
+ private schemaDir;
5
+ private schemaText;
6
+ constructor(schemaDir: string);
7
+ parse(): Promise<Model[]>;
8
+ private loadSchema;
9
+ private parseModelLine;
10
+ private postProcessRelations;
11
+ }
@@ -0,0 +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;
@@ -0,0 +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
+ }
@@ -0,0 +1,85 @@
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_FIELDS
25
+ pyContent += 'MODEL_FIELDS = {\n';
26
+ for (const model of models) {
27
+ const props = this.getAllPropertyVariations(model.name);
28
+ const fieldsStr = `{ ${model.fields.map(f => `"${f.name}": "${f.type}${f.isOptional ? '?' : ''}"`).join(', ')} }`;
29
+ for (const prop of props) {
30
+ pyContent += ` "${prop}": ${fieldsStr},\n`;
31
+ }
32
+ }
33
+ pyContent += '}\n\n';
34
+ // 3. RELATION_MAP
35
+ pyContent += 'RELATION_MAP = {\n';
36
+ for (const model of models) {
37
+ const props = this.getAllPropertyVariations(model.name);
38
+ let relationsContent = `{\n`;
39
+ for (const rel of model.relations) {
40
+ relationsContent += ` "${rel.name}": {\n`;
41
+ relationsContent += ` "modelName": "${this.toCamelCase(rel.type)}",\n`;
42
+ relationsContent += ` "relationType": "${rel.isArray ? 'many' : 'one'}",\n`;
43
+ relationsContent += ` "foreignKey": "${rel.foreignKey || 'id'}",\n`;
44
+ relationsContent += ` "localKey": "${rel.localKey || 'id'}"\n`;
45
+ relationsContent += ` },\n`;
46
+ }
47
+ relationsContent += ` }`;
48
+ for (const prop of props) {
49
+ pyContent += ` "${prop}": ${relationsContent},\n`;
50
+ }
51
+ }
52
+ pyContent += '}\n';
53
+ fs_1.default.writeFileSync(this.outputPath, pyContent);
54
+ }
55
+ getAllPropertyVariations(modelName) {
56
+ const variations = new Set();
57
+ // 1. camelCase (User -> user, McpServer -> mcpServer)
58
+ variations.add(this.toCamelCase(modelName));
59
+ // 2. snake_case (User -> user, McpServer -> mcp_server)
60
+ variations.add(this.toSnakeCase(modelName));
61
+ const acronyms = ['LLM', 'AI', 'MCP', 'IT', 'QC', 'HR', 'MR', 'WH', 'SSIS', 'API', 'URL', 'ID', 'JSON'];
62
+ for (const acronym of acronyms) {
63
+ if (modelName.startsWith(acronym)) {
64
+ variations.add(acronym.toLowerCase() + modelName.slice(acronym.length));
65
+ variations.add(acronym[0].toLowerCase() + acronym.slice(1) + modelName.slice(acronym.length));
66
+ variations.add(this.toSnakeCase(acronym.toLowerCase() + modelName.slice(acronym.length)));
67
+ }
68
+ }
69
+ return Array.from(variations);
70
+ }
71
+ toCamelCase(str) {
72
+ if (!str)
73
+ return '';
74
+ return str[0].toLowerCase() + str.slice(1);
75
+ }
76
+ toSnakeCase(str) {
77
+ if (!str)
78
+ return '';
79
+ return str
80
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
81
+ .replace(/([a-z\d])([A-Z])/g, '$1_$2')
82
+ .toLowerCase();
83
+ }
84
+ }
85
+ exports.PythonGenerator = PythonGenerator;
@@ -0,0 +1,27 @@
1
+ export interface Field {
2
+ name: string;
3
+ type: string;
4
+ sqlType: string;
5
+ isOptional: boolean;
6
+ hasDefault: boolean;
7
+ isId: boolean;
8
+ description?: string;
9
+ }
10
+ export interface Relation {
11
+ name: string;
12
+ type: string;
13
+ isArray: boolean;
14
+ isOptional: boolean;
15
+ foreignKey: string;
16
+ localKey: string;
17
+ relationName: string;
18
+ }
19
+ export interface Model {
20
+ name: string;
21
+ tableName: string;
22
+ schemaName: string;
23
+ fields: Field[];
24
+ relations: Relation[];
25
+ compoundUniques?: string[][];
26
+ description?: string;
27
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/migrate.js CHANGED
@@ -15,8 +15,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  require("dotenv/config");
16
16
  const fs_1 = __importDefault(require("fs"));
17
17
  const path_1 = __importDefault(require("path"));
18
- const an5_adapters_1 = require("an5-adapters");
19
- const rootDir = path_1.default.join(__dirname, '../');
18
+ const an5_adapters_1 = require("@an5/adapters");
19
+ const rootDir = process.cwd();
20
20
  let config = {};
21
21
  try {
22
22
  const configPath = path_1.default.join(rootDir, 'an5Orm.config.js');
@@ -295,4 +295,4 @@ main().catch((err) => {
295
295
  console.error('❌ Migration failed:', err);
296
296
  process.exit(1);
297
297
  });
298
- //# sourceMappingURL=migrate.js.map
298
+ //# sourceMappingURL=migrate.js.map
package/dist/pull.js CHANGED
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  require("dotenv/config");
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
- const an5_adapters_1 = require("an5-adapters");
10
- const rootDir = path_1.default.join(__dirname, "../");
9
+ const an5_adapters_1 = require("@an5/adapters");
10
+ const rootDir = process.cwd();
11
11
  let config = {};
12
12
  try {
13
13
  const configPath = path_1.default.join(rootDir, "an5Orm.config.js");
@@ -333,4 +333,4 @@ pull().catch((err) => {
333
333
  console.error("❌ Pull failed:", err);
334
334
  process.exit(1);
335
335
  });
336
- //# sourceMappingURL=pull.js.map
336
+ //# sourceMappingURL=pull.js.map
package/dist/push.js CHANGED
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  require("dotenv/config");
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
- const an5_adapters_1 = require("an5-adapters");
10
- const rootDir = path_1.default.join(__dirname, "../");
9
+ const adapters_1 = require("@an5/adapters");
10
+ const rootDir = process.cwd();
11
11
  let config = {};
12
12
  try {
13
13
  const configPath = path_1.default.join(rootDir, "an5Orm.config.js");
@@ -22,7 +22,7 @@ const schemaDir = path_1.default.resolve(rootDir, config.schemaDir || "an5Schema
22
22
  let _adapter = null;
23
23
  async function getDb() {
24
24
  if (!_adapter) {
25
- _adapter = new an5_adapters_1.An5Adapter({ connectionString: process.env.DATABASE_URL });
25
+ _adapter = new adapters_1.An5Adapter({ connectionString: process.env.DATABASE_URL });
26
26
  await _adapter.$connect();
27
27
  }
28
28
  return _adapter;
@@ -42,6 +42,75 @@ function parseSqlType(raw) {
42
42
  const match = raw.match(/^(\w+)/);
43
43
  return match ? match[1].toUpperCase() : raw.toUpperCase();
44
44
  }
45
+ function parseTableName(raw) {
46
+ const cleaned = raw.replace(/[\[\]"]/g, "");
47
+ const parts = cleaned.split(".").filter(Boolean);
48
+ if (parts.length >= 2) {
49
+ return { schema: parts[parts.length - 2], name: parts[parts.length - 1] };
50
+ }
51
+ return { name: cleaned };
52
+ }
53
+ function tableKey(raw) {
54
+ const table = parseTableName(raw);
55
+ return `${table.schema || "dbo"}.${table.name}`.toLowerCase();
56
+ }
57
+ function tableSqlName(raw) {
58
+ const table = parseTableName(raw);
59
+ return table.schema ? `[${table.schema}].[${table.name}]` : `[${table.name}]`;
60
+ }
61
+ function tableObjectName(raw) {
62
+ const table = parseTableName(raw);
63
+ return table.schema ? `${table.schema}.${table.name}` : table.name;
64
+ }
65
+ function safeIdentifierName(raw) {
66
+ return raw.replace(/[^A-Za-z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
67
+ }
68
+ async function dropTablesNotInSchema(models) {
69
+ var _a;
70
+ if (!((_a = config.push) === null || _a === void 0 ? void 0 : _a.dropTables)) {
71
+ return;
72
+ }
73
+ const validTableKeys = new Set(models.map(model => tableKey(model.tableName)));
74
+ const dbTables = await (await getDb()).$queryRawUnsafe(`
75
+ SELECT s.name AS schemaName, t.name AS tableName
76
+ FROM sys.tables t
77
+ INNER JOIN sys.schemas s ON s.schema_id = t.schema_id
78
+ WHERE t.is_ms_shipped = 0
79
+ `);
80
+ const tablesToDrop = dbTables.filter(table => !validTableKeys.has(`${table.schemaName}.${table.tableName}`.toLowerCase()));
81
+ if (tablesToDrop.length === 0) {
82
+ console.log("✅ No extra tables to drop.");
83
+ return;
84
+ }
85
+ console.warn(`⚠️ Dropping ${tablesToDrop.length} table(s) not present in schema because push.dropTables=true.`);
86
+ const dropKeys = new Set(tablesToDrop.map(table => `${table.schemaName}.${table.tableName}`.toLowerCase()));
87
+ const foreignKeys = await (await getDb()).$queryRawUnsafe(`
88
+ SELECT
89
+ fk.name AS constraintName,
90
+ ps.name AS parentSchema,
91
+ pt.name AS parentTable,
92
+ rs.name AS referencedSchema,
93
+ rt.name AS referencedTable
94
+ FROM sys.foreign_keys fk
95
+ INNER JOIN sys.tables pt ON pt.object_id = fk.parent_object_id
96
+ INNER JOIN sys.schemas ps ON ps.schema_id = pt.schema_id
97
+ INNER JOIN sys.tables rt ON rt.object_id = fk.referenced_object_id
98
+ INNER JOIN sys.schemas rs ON rs.schema_id = rt.schema_id
99
+ `);
100
+ for (const fk of foreignKeys) {
101
+ const parentKey = `${fk.parentSchema}.${fk.parentTable}`.toLowerCase();
102
+ const referencedKey = `${fk.referencedSchema}.${fk.referencedTable}`.toLowerCase();
103
+ if (dropKeys.has(parentKey) || dropKeys.has(referencedKey)) {
104
+ console.log(`Dropping foreign key [${fk.constraintName}] on [${fk.parentSchema}].[${fk.parentTable}]...`);
105
+ await (await getDb()).$executeRawUnsafe(`ALTER TABLE [${fk.parentSchema}].[${fk.parentTable}] DROP CONSTRAINT [${fk.constraintName}]`);
106
+ }
107
+ }
108
+ for (const table of tablesToDrop) {
109
+ console.log(`Dropping table [${table.schemaName}].[${table.tableName}]...`);
110
+ await (await getDb()).$executeRawUnsafe(`DROP TABLE [${table.schemaName}].[${table.tableName}]`);
111
+ console.log(`✅ Dropped [${table.schemaName}].[${table.tableName}]`);
112
+ }
113
+ }
45
114
  async function push() {
46
115
  let schemaText = "";
47
116
  if (fs_1.default.existsSync(schemaDir)) {
@@ -160,12 +229,15 @@ async function push() {
160
229
  }
161
230
  }
162
231
  console.log(`🚀 Pushing schema to database...`);
232
+ await dropTablesNotInSchema(models);
163
233
  for (const model of models) {
164
- console.log(`Processing table [${model.tableName}]...`);
234
+ const sqlTableName = tableSqlName(model.tableName);
235
+ const objectName = tableObjectName(model.tableName);
236
+ console.log(`Processing table ${sqlTableName}...`);
165
237
  // Check if table exists
166
- const tableExists = await (await getDb()).$queryRawUnsafe(`SELECT name FROM sys.tables WHERE name = '${model.tableName}'`);
238
+ const tableExists = await (await getDb()).$queryRawUnsafe(`SELECT name FROM sys.tables WHERE object_id = OBJECT_ID('${objectName}')`);
167
239
  if (tableExists.length === 0) {
168
- console.log(`Creating table [${model.tableName}]...`);
240
+ console.log(`Creating table ${sqlTableName}...`);
169
241
  const colDefs = model.fields.map((f) => {
170
242
  let def = `[${f.name}] ${f.sqlType}`;
171
243
  if (f.isId)
@@ -181,32 +253,32 @@ async function push() {
181
253
  // Add compound uniques as table constraints
182
254
  if (model.compoundUniques && model.compoundUniques.length > 0) {
183
255
  model.compoundUniques.forEach((fields, idx) => {
184
- const constraintName = `UQ_${model.tableName}_compound_${idx}`;
256
+ const constraintName = `UQ_${safeIdentifierName(model.tableName)}_compound_${idx}`;
185
257
  const fieldsStr = fields.map(f => `[${f}]`).join(", ");
186
258
  colDefs.push(`CONSTRAINT [${constraintName}] UNIQUE (${fieldsStr})`);
187
259
  });
188
260
  }
189
- const createSql = `CREATE TABLE [${model.tableName}] (\n ${colDefs.join(",\n ")}\n)`;
261
+ const createSql = `CREATE TABLE ${sqlTableName} (\n ${colDefs.join(",\n ")}\n)`;
190
262
  await (await getDb()).$executeRawUnsafe(createSql);
191
- console.log(`✅ Table [${model.tableName}] created.`);
263
+ console.log(`✅ Table ${sqlTableName} created.`);
192
264
  // Create indexes
193
265
  if (model.indexes && model.indexes.length > 0) {
194
266
  for (let idx = 0; idx < model.indexes.length; idx++) {
195
267
  const fields = model.indexes[idx];
196
- const indexName = `IX_${model.tableName}_${fields.join("_")}`;
268
+ const indexName = `IX_${safeIdentifierName(model.tableName)}_${fields.join("_")}`;
197
269
  const fieldsStr = fields.map((f) => `[${f}]`).join(", ");
198
- console.log(`Creating index [${indexName}] on table [${model.tableName}]...`);
199
- await (await getDb()).$executeRawUnsafe(`CREATE INDEX [${indexName}] ON [${model.tableName}] (${fieldsStr})`);
270
+ console.log(`Creating index [${indexName}] on table ${sqlTableName}...`);
271
+ await (await getDb()).$executeRawUnsafe(`CREATE INDEX [${indexName}] ON ${sqlTableName} (${fieldsStr})`);
200
272
  }
201
273
  }
202
274
  }
203
275
  else {
204
276
  // 1. Check for missing columns
205
277
  for (const field of model.fields) {
206
- const colExists = await (await getDb()).$queryRawUnsafe(`SELECT name FROM sys.columns WHERE object_id = OBJECT_ID('${model.tableName}') AND name = '${field.name}'`);
278
+ const colExists = await (await getDb()).$queryRawUnsafe(`SELECT name FROM sys.columns WHERE object_id = OBJECT_ID('${objectName}') AND name = '${field.name}'`);
207
279
  if (colExists.length === 0) {
208
- console.log(`Adding column [${field.name}] to table [${model.tableName}]...`);
209
- let alterSql = `ALTER TABLE [${model.tableName}] ADD [${field.name}] ${field.sqlType}`;
280
+ console.log(`Adding column [${field.name}] to table ${sqlTableName}...`);
281
+ let alterSql = `ALTER TABLE ${sqlTableName} ADD [${field.name}] ${field.sqlType}`;
210
282
  if (field.defaultValue)
211
283
  alterSql += ` ${field.defaultValue}`;
212
284
  if (field.isUnique && !field.isId)
@@ -224,12 +296,12 @@ async function push() {
224
296
  if (model.compoundUniques && model.compoundUniques.length > 0) {
225
297
  for (let idx = 0; idx < model.compoundUniques.length; idx++) {
226
298
  const fields = model.compoundUniques[idx];
227
- const constraintName = `UQ_${model.tableName}_compound_${idx}`;
228
- const constraintExists = await (await getDb()).$queryRawUnsafe(`SELECT name FROM sys.objects WHERE type = 'UQ' AND parent_object_id = OBJECT_ID('${model.tableName}') AND name = '${constraintName}'`);
299
+ const constraintName = `UQ_${safeIdentifierName(model.tableName)}_compound_${idx}`;
300
+ const constraintExists = await (await getDb()).$queryRawUnsafe(`SELECT name FROM sys.objects WHERE type = 'UQ' AND parent_object_id = OBJECT_ID('${objectName}') AND name = '${constraintName}'`);
229
301
  if (constraintExists.length === 0) {
230
- console.log(`Adding compound unique constraint [${constraintName}] to table [${model.tableName}]...`);
302
+ console.log(`Adding compound unique constraint [${constraintName}] to table ${sqlTableName}...`);
231
303
  const fieldsStr = fields.map((f) => `[${f}]`).join(", ");
232
- await (await getDb()).$executeRawUnsafe(`ALTER TABLE [${model.tableName}] ADD CONSTRAINT [${constraintName}] UNIQUE (${fieldsStr})`);
304
+ await (await getDb()).$executeRawUnsafe(`ALTER TABLE ${sqlTableName} ADD CONSTRAINT [${constraintName}] UNIQUE (${fieldsStr})`);
233
305
  }
234
306
  }
235
307
  }
@@ -237,12 +309,12 @@ async function push() {
237
309
  if (model.indexes && model.indexes.length > 0) {
238
310
  for (let idx = 0; idx < model.indexes.length; idx++) {
239
311
  const fields = model.indexes[idx];
240
- const indexName = `IX_${model.tableName}_${fields.join("_")}`;
241
- const indexExists = await (await getDb()).$queryRawUnsafe(`SELECT name FROM sys.indexes WHERE object_id = OBJECT_ID('${model.tableName}') AND name = '${indexName}'`);
312
+ const indexName = `IX_${safeIdentifierName(model.tableName)}_${fields.join("_")}`;
313
+ const indexExists = await (await getDb()).$queryRawUnsafe(`SELECT name FROM sys.indexes WHERE object_id = OBJECT_ID('${objectName}') AND name = '${indexName}'`);
242
314
  if (indexExists.length === 0) {
243
- console.log(`Creating index [${indexName}] on table [${model.tableName}]...`);
315
+ console.log(`Creating index [${indexName}] on table ${sqlTableName}...`);
244
316
  const fieldsStr = fields.map((f) => `[${f}]`).join(", ");
245
- await (await getDb()).$executeRawUnsafe(`CREATE INDEX [${indexName}] ON [${model.tableName}] (${fieldsStr})`);
317
+ await (await getDb()).$executeRawUnsafe(`CREATE INDEX [${indexName}] ON ${sqlTableName} (${fieldsStr})`);
246
318
  }
247
319
  }
248
320
  }
@@ -255,4 +327,3 @@ push().catch((err) => {
255
327
  console.error("❌ Push failed:", err);
256
328
  process.exit(1);
257
329
  });
258
- //# sourceMappingURL=push.js.map
@@ -24,7 +24,7 @@ function clearGeneratedFiles(outputDir: string, extension: string) {
24
24
  }
25
25
 
26
26
  async function main() {
27
- const rootDir = path.join(__dirname, '../../../');
27
+ const rootDir = process.cwd();
28
28
  let config: any = {};
29
29
 
30
30
  try {