@an5/orm 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/pull.js ADDED
@@ -0,0 +1,336 @@
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
+ require("dotenv/config");
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const an5_adapters_1 = require("an5-adapters");
10
+ const rootDir = path_1.default.join(__dirname, "../");
11
+ let config = {};
12
+ try {
13
+ const configPath = path_1.default.join(rootDir, "an5Orm.config.js");
14
+ if (fs_1.default.existsSync(configPath)) {
15
+ config = require(configPath);
16
+ }
17
+ }
18
+ catch (err) {
19
+ console.warn("⚠️ Could not load config file in pull.ts, using defaults.");
20
+ }
21
+ const schemaDir = path_1.default.resolve(rootDir, config.schemaDir || "an5Schema");
22
+ let _adapter = null;
23
+ async function getDb() {
24
+ if (!_adapter) {
25
+ _adapter = new an5_adapters_1.An5Adapter({ connectionString: process.env.DATABASE_URL });
26
+ await _adapter.$connect();
27
+ }
28
+ return _adapter;
29
+ }
30
+ function capitalize(str) {
31
+ return str.charAt(0).toUpperCase() + str.slice(1);
32
+ }
33
+ async function pull() {
34
+ console.log("🔍 Pulling schema from database...");
35
+ const tables = await (await getDb()).$queryRawUnsafe(`
36
+ SELECT s.name AS schemaName, t.name AS tableName
37
+ FROM sys.tables t
38
+ JOIN sys.schemas s ON t.schema_id = s.schema_id
39
+ WHERE t.is_ms_shipped = 0
40
+ ORDER BY t.name
41
+ `);
42
+ // Load existing schema files
43
+ const fileContents = {};
44
+ const modelToFileMap = {}; // modelName -> fileName
45
+ const tableToFileMap = {}; // tableName -> fileName
46
+ if (fs_1.default.existsSync(schemaDir)) {
47
+ const files = fs_1.default.readdirSync(schemaDir).filter(f => f.endsWith(".an5"));
48
+ for (const file of files) {
49
+ const fullPath = path_1.default.join(schemaDir, file);
50
+ const text = fs_1.default.readFileSync(fullPath, "utf8");
51
+ fileContents[file] = text;
52
+ // Extract model definitions and table mappings from existing file contents
53
+ const modelRegex = /model\s+(\w+)\s*\{([^}]*)\}/g;
54
+ let match;
55
+ while ((match = modelRegex.exec(text)) !== null) {
56
+ const modelName = match[1];
57
+ const block = match[2];
58
+ modelToFileMap[modelName] = file;
59
+ // Try to find @@map
60
+ const mapMatch = block.match(/@@map\("(.+)"\)/);
61
+ if (mapMatch) {
62
+ const tableName = mapMatch[1];
63
+ tableToFileMap[tableName] = file;
64
+ }
65
+ else {
66
+ // Default pluralization mapping
67
+ const defaultTableName = modelName.toLowerCase() + "s";
68
+ tableToFileMap[defaultTableName] = file;
69
+ }
70
+ }
71
+ }
72
+ }
73
+ else {
74
+ fs_1.default.mkdirSync(schemaDir, { recursive: true });
75
+ }
76
+ // Ensure default core.an5 exists in fileContents
77
+ const defaultFile = "core.an5";
78
+ if (!fileContents[defaultFile]) {
79
+ fileContents[defaultFile] = "";
80
+ }
81
+ const existingModelNames = new Set(Object.keys(modelToFileMap));
82
+ const generatedModels = {};
83
+ for (const table of tables) {
84
+ const { tableName } = table;
85
+ console.log(`Introspecting table [${tableName}]...`);
86
+ // Get columns
87
+ const columns = await (await getDb()).$queryRawUnsafe(`
88
+ SELECT
89
+ c.name AS columnName,
90
+ ty.name AS dataType,
91
+ c.max_length AS maxLength,
92
+ c.precision AS precision,
93
+ c.scale AS scale,
94
+ c.is_nullable AS isNullable,
95
+ c.is_identity AS isIdentity,
96
+ pk.is_primary_key AS isPrimaryKey,
97
+ d.definition AS defaultValue
98
+ FROM sys.columns c
99
+ JOIN sys.types ty ON c.user_type_id = ty.user_type_id
100
+ LEFT JOIN (
101
+ SELECT ic.object_id, ic.column_id, i.is_primary_key
102
+ FROM sys.index_columns ic
103
+ JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
104
+ WHERE i.is_primary_key = 1
105
+ ) pk ON c.object_id = pk.object_id AND c.column_id = pk.column_id
106
+ LEFT JOIN sys.default_constraints d ON c.default_object_id = d.object_id
107
+ WHERE c.object_id = OBJECT_ID(@p_0)
108
+ ORDER BY c.column_id
109
+ `, tableName);
110
+ // Get indexes
111
+ const indexes = await (await getDb()).$queryRawUnsafe(`
112
+ SELECT
113
+ i.name AS indexName,
114
+ i.is_unique AS isUnique,
115
+ i.is_primary_key AS isPrimaryKey,
116
+ c.name AS columnName,
117
+ ic.key_ordinal AS keyOrdinal
118
+ FROM sys.indexes i
119
+ JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
120
+ JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
121
+ WHERE i.object_id = OBJECT_ID(@p_0) AND i.is_hypothetical = 0 AND i.name IS NOT NULL
122
+ ORDER BY i.name, ic.key_ordinal
123
+ `, tableName);
124
+ const indexGroups = {};
125
+ for (const idx of indexes) {
126
+ if (!indexGroups[idx.indexName]) {
127
+ indexGroups[idx.indexName] = {
128
+ isUnique: idx.isUnique === 1 || idx.isUnique === true,
129
+ isPrimaryKey: idx.isPrimaryKey === 1 || idx.isPrimaryKey === true,
130
+ columns: []
131
+ };
132
+ }
133
+ indexGroups[idx.indexName].columns.push(idx.columnName);
134
+ }
135
+ const uniqueColumns = new Set();
136
+ const compoundUniques = [];
137
+ const modelIndexes = [];
138
+ for (const [name, info] of Object.entries(indexGroups)) {
139
+ if (info.isPrimaryKey)
140
+ continue;
141
+ if (info.isUnique) {
142
+ if (info.columns.length === 1) {
143
+ uniqueColumns.add(info.columns[0]);
144
+ }
145
+ else {
146
+ compoundUniques.push(info.columns);
147
+ }
148
+ }
149
+ else {
150
+ modelIndexes.push(info.columns);
151
+ }
152
+ }
153
+ let modelName = "";
154
+ for (const [mName, file] of Object.entries(modelToFileMap)) {
155
+ const text = fileContents[file];
156
+ const modelRegex = new RegExp(`model\\s+(${mName})\\s*\\{[^}]*\\}`, 'g');
157
+ let m;
158
+ while ((m = modelRegex.exec(text)) !== null) {
159
+ const block = m[0];
160
+ const mapMatch = block.match(/@@map\("(.+)"\)/);
161
+ if (mapMatch && mapMatch[1] === tableName) {
162
+ modelName = mName;
163
+ break;
164
+ }
165
+ }
166
+ if (modelName)
167
+ break;
168
+ }
169
+ if (!modelName) {
170
+ modelName = tableName.charAt(0).toUpperCase() + tableName.slice(1).replace(/s$/, "");
171
+ }
172
+ // Parse existing model fields from file for merging
173
+ const existingFields = {};
174
+ const targetFile = modelToFileMap[modelName] || defaultFile;
175
+ const fileContent = fileContents[targetFile];
176
+ if (fileContent) {
177
+ const modelRegex = new RegExp(`model\\s+${modelName}\\s*\\{([^}]*)\\}`, 'g');
178
+ const match = modelRegex.exec(fileContent);
179
+ if (match) {
180
+ const block = match[1];
181
+ const lines = block.split("\n");
182
+ for (let line of lines) {
183
+ line = line.trim();
184
+ if (!line || line.startsWith("@@") || line.startsWith("model ") || line === "}")
185
+ continue;
186
+ const parts = line.split(/\s+/);
187
+ const name = parts[0];
188
+ const type = parts[1];
189
+ if (!name || !type)
190
+ continue;
191
+ const cleanType = type.replace("[]", "").replace("?", "");
192
+ const isRelation = existingModelNames.has(cleanType) || type.endsWith("[]");
193
+ existingFields[name.toLowerCase()] = {
194
+ line,
195
+ isRelation,
196
+ name
197
+ };
198
+ }
199
+ }
200
+ }
201
+ let modelStr = `model ${modelName} {\n`;
202
+ for (const col of columns) {
203
+ const { columnName, dataType, isNullable, isPrimaryKey, isIdentity, defaultValue } = col;
204
+ // Generate SQL Server type directly
205
+ let sqlType = dataType.toUpperCase();
206
+ const dt = dataType.toLowerCase();
207
+ // Add length/precision params
208
+ if (dt === "nvarchar" || dt === "varchar") {
209
+ sqlType = col.maxLength === -1 ? `${sqlType.toUpperCase()}(MAX)` : `${sqlType.toUpperCase()}(${col.maxLength / 2})`;
210
+ }
211
+ else if (dt === "char" || dt === "nchar") {
212
+ sqlType = `${sqlType.toUpperCase()}(${col.maxLength / 2})`;
213
+ }
214
+ else if (dt === "varbinary" || dt === "binary") {
215
+ sqlType = col.maxLength === -1 ? `${sqlType.toUpperCase()}(MAX)` : `${sqlType.toUpperCase()}(${col.maxLength})`;
216
+ }
217
+ else if (dt === "decimal" || dt === "numeric") {
218
+ sqlType = `${sqlType.toUpperCase()}(${col.precision}, ${col.scale})`;
219
+ }
220
+ const optional = isNullable ? "?" : "";
221
+ let attributes = "";
222
+ if (isPrimaryKey)
223
+ attributes += " @id";
224
+ if (isIdentity)
225
+ attributes += " @default(autoincrement())";
226
+ if (uniqueColumns.has(columnName) && !isPrimaryKey)
227
+ attributes += " @unique";
228
+ if (defaultValue) {
229
+ if (defaultValue.includes("newid()")) {
230
+ attributes += " @default(uuid())";
231
+ }
232
+ else if (defaultValue.includes("getdate()") || defaultValue.includes("sysdatetime()")) {
233
+ attributes += " @default(now())";
234
+ }
235
+ else {
236
+ const cleaned = defaultValue.replace(/^\(+(.*?)\)+$/, '$1');
237
+ if (dt === "bit") {
238
+ if (cleaned === "1" || cleaned.toLowerCase() === "true")
239
+ attributes += " @default(true)";
240
+ else if (cleaned === "0" || cleaned.toLowerCase() === "false")
241
+ attributes += " @default(false)";
242
+ }
243
+ else if (dt === "int" || dt === "float" || dt === "bigint" || dt === "decimal" || dt === "numeric") {
244
+ if (!isNaN(Number(cleaned)))
245
+ attributes += ` @default(${cleaned})`;
246
+ }
247
+ else if (dt === "nvarchar" || dt === "varchar") {
248
+ if (cleaned.startsWith("'") && cleaned.endsWith("'")) {
249
+ const val = cleaned.slice(1, -1).replace(/"/g, '\\"');
250
+ attributes += ` @default("${val}")`;
251
+ }
252
+ }
253
+ }
254
+ }
255
+ let fieldLine = `${columnName} ${sqlType}${optional}${attributes}`;
256
+ // Merge decorators/attributes from existing schema
257
+ const existing = existingFields[columnName.toLowerCase()];
258
+ if (existing) {
259
+ const attributesMatch = existing.line.match(/(@default\(.*?\)|@updatedAt)/g);
260
+ if (attributesMatch) {
261
+ for (const attr of attributesMatch) {
262
+ if (!fieldLine.includes(attr)) {
263
+ if (attr.startsWith("@default") && fieldLine.includes("@default")) {
264
+ continue;
265
+ }
266
+ fieldLine += ` ${attr}`;
267
+ }
268
+ }
269
+ }
270
+ }
271
+ modelStr += ` ${fieldLine}\n`;
272
+ }
273
+ // Append preserved relations
274
+ for (const [lowerName, fieldInfo] of Object.entries(existingFields)) {
275
+ if (fieldInfo.isRelation) {
276
+ modelStr += ` ${fieldInfo.line}\n`;
277
+ }
278
+ }
279
+ const uniqueStrings = new Set();
280
+ for (const cols of compoundUniques) {
281
+ uniqueStrings.add(`@@unique([${cols.join(", ")}])`);
282
+ }
283
+ for (const key of uniqueStrings) {
284
+ modelStr += ` ${key}\n`;
285
+ }
286
+ const indexStrings = new Set();
287
+ for (const cols of modelIndexes) {
288
+ indexStrings.add(`@@index([${cols.join(", ")}])`);
289
+ }
290
+ for (const key of indexStrings) {
291
+ modelStr += ` ${key}\n`;
292
+ }
293
+ if (tableName !== modelName.toLowerCase() + "s") {
294
+ modelStr += `\n @@map("${tableName}")\n`;
295
+ }
296
+ modelStr += "}";
297
+ generatedModels[modelName] = modelStr;
298
+ }
299
+ for (const [modelName, modelStr] of Object.entries(generatedModels)) {
300
+ const targetFile = modelToFileMap[modelName] || defaultFile;
301
+ const fileContent = fileContents[targetFile];
302
+ const modelRegex = new RegExp(`model\\s+${modelName}\\s*\\{[^}]*\\}`, 'g');
303
+ if (modelRegex.test(fileContent)) {
304
+ fileContents[targetFile] = fileContent.replace(modelRegex, modelStr);
305
+ console.log(`✏️ Updated model [${modelName}] in file [${targetFile}]`);
306
+ }
307
+ else {
308
+ fileContents[targetFile] = fileContent.trim() + "\n\n" + modelStr + "\n";
309
+ console.log(`➕ Added model [${modelName}] to file [${targetFile}]`);
310
+ }
311
+ }
312
+ for (const [file, content] of Object.entries(fileContents)) {
313
+ const fullPath = path_1.default.join(schemaDir, file);
314
+ fs_1.default.writeFileSync(fullPath, content.trim() + "\n");
315
+ console.log(`💾 Saved updates to [${fullPath}]`);
316
+ }
317
+ // Index schema into vector store for RAG
318
+ console.log("\n📚 Indexing schema into vector store for RAG...");
319
+ try {
320
+ const { indexSchema, indexQuerySamples } = require(path_1.default.join(rootDir, "an5Agent", "dist", "rag", "indexer.js"));
321
+ await indexSchema(schemaDir);
322
+ await indexQuerySamples();
323
+ console.log("✅ Schema indexed for RAG.");
324
+ }
325
+ catch (err) {
326
+ console.warn(`⚠️ RAG indexing skipped: ${err.message || err}`);
327
+ console.warn(` Run 'npm --prefix an5Agent run rag:index' manually to build vector store.`);
328
+ }
329
+ console.log("✅ Database schema pulling completed successfully.");
330
+ process.exit(0);
331
+ }
332
+ pull().catch((err) => {
333
+ console.error("❌ Pull failed:", err);
334
+ process.exit(1);
335
+ });
336
+ //# sourceMappingURL=pull.js.map
package/dist/push.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import "dotenv/config";
2
+ //# sourceMappingURL=push.d.ts.map
package/dist/push.js ADDED
@@ -0,0 +1,258 @@
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
+ require("dotenv/config");
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const an5_adapters_1 = require("an5-adapters");
10
+ const rootDir = path_1.default.join(__dirname, "../");
11
+ let config = {};
12
+ try {
13
+ const configPath = path_1.default.join(rootDir, "an5Orm.config.js");
14
+ if (fs_1.default.existsSync(configPath)) {
15
+ config = require(configPath);
16
+ }
17
+ }
18
+ catch (err) {
19
+ console.warn("⚠️ Could not load config file in push.ts, using defaults.");
20
+ }
21
+ const schemaDir = path_1.default.resolve(rootDir, config.schemaDir || "an5Schema");
22
+ let _adapter = null;
23
+ async function getDb() {
24
+ if (!_adapter) {
25
+ _adapter = new an5_adapters_1.An5Adapter({ connectionString: process.env.DATABASE_URL });
26
+ await _adapter.$connect();
27
+ }
28
+ return _adapter;
29
+ }
30
+ // Supported SQL Server types (base types without params)
31
+ const AN5_TYPES = new Set([
32
+ "NVARCHAR", "VARCHAR", "CHAR", "NCHAR", "TEXT", "NTEXT", "XML",
33
+ "INT", "SMALLINT", "TINYINT", "BIGINT", "FLOAT", "REAL", "DECIMAL", "NUMERIC",
34
+ "MONEY", "SMALLMONEY", "BIT",
35
+ "DATETIME", "DATETIME2", "SMALLDATETIME", "DATE", "TIME", "DATETIMEOFFSET",
36
+ "VARBINARY", "BINARY", "IMAGE",
37
+ "UNIQUEIDENTIFIER", "SQL_VARIANT", "ROWVERSION",
38
+ "HIERARCHYID", "GEOGRAPHY", "GEOMETRY", "VECTOR",
39
+ ]);
40
+ // Parse base type from "NVARCHAR(255)" → "NVARCHAR"
41
+ function parseSqlType(raw) {
42
+ const match = raw.match(/^(\w+)/);
43
+ return match ? match[1].toUpperCase() : raw.toUpperCase();
44
+ }
45
+ async function push() {
46
+ let schemaText = "";
47
+ if (fs_1.default.existsSync(schemaDir)) {
48
+ const files = fs_1.default.readdirSync(schemaDir).filter(f => f.endsWith(".an5"));
49
+ for (const file of files) {
50
+ schemaText += fs_1.default.readFileSync(path_1.default.join(schemaDir, file), "utf8") + "\n";
51
+ }
52
+ }
53
+ else {
54
+ const schemaPath = path_1.default.join(__dirname, "schema.an5");
55
+ if (fs_1.default.existsSync(schemaPath)) {
56
+ schemaText = fs_1.default.readFileSync(schemaPath, "utf8");
57
+ }
58
+ else {
59
+ console.error(`No schema found in ${schemaDir} or schema.an5`);
60
+ process.exit(1);
61
+ }
62
+ }
63
+ const lines = schemaText.split("\n");
64
+ const models = [];
65
+ let currentModel = null;
66
+ for (let line of lines) {
67
+ line = line.trim();
68
+ if (!line || line.startsWith("//"))
69
+ continue;
70
+ const modelHeaderMatch = line.match(/^model\s+(\w+)\s*\{/);
71
+ if (modelHeaderMatch) {
72
+ currentModel = {
73
+ name: modelHeaderMatch[1],
74
+ tableName: modelHeaderMatch[1].toLowerCase() + "s",
75
+ fields: [],
76
+ compoundUniques: [],
77
+ indexes: [],
78
+ };
79
+ models.push(currentModel);
80
+ continue;
81
+ }
82
+ if (line === "}") {
83
+ currentModel = null;
84
+ continue;
85
+ }
86
+ if (currentModel) {
87
+ if (line.startsWith("@@map")) {
88
+ const mapMatch = line.match(/@@map\("(.+)"\)/);
89
+ if (mapMatch)
90
+ currentModel.tableName = mapMatch[1];
91
+ continue;
92
+ }
93
+ if (line.startsWith("@@unique")) {
94
+ const uniqueMatch = line.match(/@@unique\(\[([\w,\s]+)\]\)/);
95
+ if (uniqueMatch) {
96
+ const fields = uniqueMatch[1].split(",").map(f => f.trim());
97
+ currentModel.compoundUniques.push(fields);
98
+ }
99
+ continue;
100
+ }
101
+ if (line.startsWith("@@index")) {
102
+ const indexMatch = line.match(/@@index\(\[([\w,\s]+)\]\)/);
103
+ if (indexMatch) {
104
+ const fields = indexMatch[1].split(",").map(f => f.trim());
105
+ currentModel.indexes.push(fields);
106
+ }
107
+ continue;
108
+ }
109
+ if (line.startsWith("@@"))
110
+ continue;
111
+ const parts = line.split(/\s+/);
112
+ const fieldName = parts[0];
113
+ const fieldType = parts[1];
114
+ if (!fieldName || !fieldType)
115
+ continue;
116
+ // Parse SQL Server type directly
117
+ const cleanType = fieldType.replace("[]", "").replace("?", "");
118
+ const sqlBase = parseSqlType(cleanType);
119
+ // Skip if not a known SQL Server type (might be a relation)
120
+ if (!AN5_TYPES.has(sqlBase)) {
121
+ continue;
122
+ }
123
+ const isOptional = fieldType.endsWith("?");
124
+ const isId = line.includes("@id");
125
+ const isUnique = line.includes("@unique");
126
+ // Use SQL Server type directly - no mapping needed
127
+ const sqlType = cleanType.toUpperCase();
128
+ let defaultValue = "";
129
+ const defaultMatch = line.match(/@default\((.*)\)/);
130
+ if (defaultMatch) {
131
+ const val = defaultMatch[1];
132
+ if (val === "uuid()")
133
+ defaultValue = "DEFAULT NEWID()";
134
+ else if (val === "cuid()")
135
+ defaultValue = "DEFAULT NEWID()";
136
+ else if (val === "now()")
137
+ defaultValue = "DEFAULT CURRENT_TIMESTAMP";
138
+ else if (val === "autoincrement()")
139
+ defaultValue = "IDENTITY(1,1)";
140
+ else if (val === "true")
141
+ defaultValue = "DEFAULT 1";
142
+ else if (val === "false")
143
+ defaultValue = "DEFAULT 0";
144
+ else if (/^".*"$/.test(val))
145
+ defaultValue = `DEFAULT '${val.slice(1, -1)}'`;
146
+ else
147
+ defaultValue = `DEFAULT ${val}`;
148
+ }
149
+ else if (line.includes("@updatedAt")) {
150
+ defaultValue = "DEFAULT CURRENT_TIMESTAMP";
151
+ }
152
+ currentModel.fields.push({
153
+ name: fieldName,
154
+ sqlType,
155
+ isOptional,
156
+ isId,
157
+ isUnique,
158
+ defaultValue,
159
+ });
160
+ }
161
+ }
162
+ console.log(`🚀 Pushing schema to database...`);
163
+ for (const model of models) {
164
+ console.log(`Processing table [${model.tableName}]...`);
165
+ // Check if table exists
166
+ const tableExists = await (await getDb()).$queryRawUnsafe(`SELECT name FROM sys.tables WHERE name = '${model.tableName}'`);
167
+ if (tableExists.length === 0) {
168
+ console.log(`Creating table [${model.tableName}]...`);
169
+ const colDefs = model.fields.map((f) => {
170
+ let def = `[${f.name}] ${f.sqlType}`;
171
+ if (f.isId)
172
+ def += " PRIMARY KEY";
173
+ if (f.defaultValue)
174
+ def += ` ${f.defaultValue}`;
175
+ if (!f.isOptional && !f.defaultValue && !f.isId)
176
+ def += " NOT NULL";
177
+ if (f.isUnique && !f.isId)
178
+ def += " UNIQUE";
179
+ return def;
180
+ });
181
+ // Add compound uniques as table constraints
182
+ if (model.compoundUniques && model.compoundUniques.length > 0) {
183
+ model.compoundUniques.forEach((fields, idx) => {
184
+ const constraintName = `UQ_${model.tableName}_compound_${idx}`;
185
+ const fieldsStr = fields.map(f => `[${f}]`).join(", ");
186
+ colDefs.push(`CONSTRAINT [${constraintName}] UNIQUE (${fieldsStr})`);
187
+ });
188
+ }
189
+ const createSql = `CREATE TABLE [${model.tableName}] (\n ${colDefs.join(",\n ")}\n)`;
190
+ await (await getDb()).$executeRawUnsafe(createSql);
191
+ console.log(`✅ Table [${model.tableName}] created.`);
192
+ // Create indexes
193
+ if (model.indexes && model.indexes.length > 0) {
194
+ for (let idx = 0; idx < model.indexes.length; idx++) {
195
+ const fields = model.indexes[idx];
196
+ const indexName = `IX_${model.tableName}_${fields.join("_")}`;
197
+ 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})`);
200
+ }
201
+ }
202
+ }
203
+ else {
204
+ // 1. Check for missing columns
205
+ 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}'`);
207
+ 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}`;
210
+ if (field.defaultValue)
211
+ alterSql += ` ${field.defaultValue}`;
212
+ if (field.isUnique && !field.isId)
213
+ alterSql += " UNIQUE";
214
+ if (!field.isOptional && !field.defaultValue) {
215
+ if (!field.defaultValue)
216
+ alterSql += " NULL";
217
+ else
218
+ alterSql += " NOT NULL";
219
+ }
220
+ await (await getDb()).$executeRawUnsafe(alterSql);
221
+ }
222
+ }
223
+ // 2. Check for missing compound uniques
224
+ if (model.compoundUniques && model.compoundUniques.length > 0) {
225
+ for (let idx = 0; idx < model.compoundUniques.length; idx++) {
226
+ 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}'`);
229
+ if (constraintExists.length === 0) {
230
+ console.log(`Adding compound unique constraint [${constraintName}] to table [${model.tableName}]...`);
231
+ const fieldsStr = fields.map((f) => `[${f}]`).join(", ");
232
+ await (await getDb()).$executeRawUnsafe(`ALTER TABLE [${model.tableName}] ADD CONSTRAINT [${constraintName}] UNIQUE (${fieldsStr})`);
233
+ }
234
+ }
235
+ }
236
+ // 3. Check for missing indexes
237
+ if (model.indexes && model.indexes.length > 0) {
238
+ for (let idx = 0; idx < model.indexes.length; idx++) {
239
+ 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}'`);
242
+ if (indexExists.length === 0) {
243
+ console.log(`Creating index [${indexName}] on table [${model.tableName}]...`);
244
+ const fieldsStr = fields.map((f) => `[${f}]`).join(", ");
245
+ await (await getDb()).$executeRawUnsafe(`CREATE INDEX [${indexName}] ON [${model.tableName}] (${fieldsStr})`);
246
+ }
247
+ }
248
+ }
249
+ }
250
+ }
251
+ console.log("✅ Database push completed.");
252
+ process.exit(0);
253
+ }
254
+ push().catch((err) => {
255
+ console.error("❌ Push failed:", err);
256
+ process.exit(1);
257
+ });
258
+ //# sourceMappingURL=push.js.map
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env tsx
2
+ export {};
3
+ //# sourceMappingURL=release-cli.d.ts.map