@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.
@@ -0,0 +1,2 @@
1
+ import "dotenv/config";
2
+ //# sourceMappingURL=cleanup.d.ts.map
@@ -0,0 +1,117 @@
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 an5_adapters_1 = require("an5-adapters");
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
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 cleanup.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
+ async function cleanup() {
31
+ console.log("๐Ÿงน Starting database cleanup...");
32
+ let schemaText = "";
33
+ if (fs_1.default.existsSync(schemaDir)) {
34
+ const files = fs_1.default.readdirSync(schemaDir).filter(f => f.endsWith(".an5"));
35
+ for (const file of files) {
36
+ schemaText += fs_1.default.readFileSync(path_1.default.join(schemaDir, file), "utf8") + "\n";
37
+ }
38
+ }
39
+ else {
40
+ const schemaPath = path_1.default.join(__dirname, "schema.an5");
41
+ if (fs_1.default.existsSync(schemaPath)) {
42
+ schemaText = fs_1.default.readFileSync(schemaPath, "utf8");
43
+ }
44
+ else {
45
+ console.error("No schema found!");
46
+ process.exit(1);
47
+ }
48
+ }
49
+ const lines = schemaText.split("\n");
50
+ const validTables = new Set();
51
+ for (let line of lines) {
52
+ line = line.trim();
53
+ if (line.startsWith("model ")) {
54
+ const modelName = line.split(" ")[1].replace("{", "").trim();
55
+ // Default table name is model name + 's' (lowercase)
56
+ validTables.add(modelName.toLowerCase() + "s");
57
+ }
58
+ if (line.startsWith("@@map")) {
59
+ const mapMatch = line.match(/@@map\("(.+)"\)/);
60
+ if (mapMatch) {
61
+ // The last model's default name should be replaced by the map name
62
+ // Since we add the default name first, we need to be careful.
63
+ // Actually, let's just add the map name.
64
+ validTables.add(mapMatch[1]);
65
+ }
66
+ }
67
+ }
68
+ // Also include the capitalization variations from an5Metadata if we can,
69
+ // but schema.an5 is the source of truth.
70
+ console.log("Valid tables from schema:", Array.from(validTables));
71
+ const dbTables = await (await getDb()).$queryRawUnsafe(`
72
+ SELECT name FROM sys.tables WHERE is_ms_shipped = 0
73
+ `);
74
+ const tablesToDrop = dbTables
75
+ .map(t => t.name)
76
+ .filter(name => !validTables.has(name) && !validTables.has(name.toLowerCase()));
77
+ if (tablesToDrop.length === 0) {
78
+ console.log("โœ… No extra tables found. Database is clean.");
79
+ process.exit(0);
80
+ }
81
+ console.log("Tables to be DROPPED:", tablesToDrop);
82
+ for (const table of tablesToDrop) {
83
+ console.log(`Dropping table [${table}]...`);
84
+ try {
85
+ // We need to handle foreign key constraints.
86
+ // For a thorough cleanup, we might need to drop constraints first.
87
+ // But for now, we'll try a simple DROP TABLE.
88
+ await (await getDb()).$executeRawUnsafe(`DROP TABLE [${table}]`);
89
+ console.log(`โœ… Dropped [${table}]`);
90
+ }
91
+ catch (err) {
92
+ console.error(`โŒ Failed to drop [${table}]: ${err.message}`);
93
+ if (err.message.includes("FOREIGN KEY constraint")) {
94
+ console.log(`Attempting to find and drop constraints for [${table}]...`);
95
+ const constraints = await (await getDb()).$queryRawUnsafe(`
96
+ SELECT fk.name AS constraint_name, OBJECT_NAME(fk.parent_object_id) AS table_name
97
+ FROM sys.foreign_keys AS fk
98
+ WHERE OBJECT_NAME(fk.referenced_object_id) = @p_0 OR OBJECT_NAME(fk.parent_object_id) = @p_0
99
+ `, table);
100
+ for (const c of constraints) {
101
+ console.log(`Dropping constraint [${c.constraint_name}] on [${c.table_name}]...`);
102
+ await (await getDb()).$executeRawUnsafe(`ALTER TABLE [${c.table_name}] DROP CONSTRAINT [${c.constraint_name}]`);
103
+ }
104
+ // Try dropping again
105
+ await (await getDb()).$executeRawUnsafe(`DROP TABLE [${table}]`);
106
+ console.log(`โœ… Dropped [${table}] after removing constraints.`);
107
+ }
108
+ }
109
+ }
110
+ console.log("โœ… Cleanup completed.");
111
+ process.exit(0);
112
+ }
113
+ cleanup().catch(err => {
114
+ console.error("โŒ Cleanup failed:", err);
115
+ process.exit(1);
116
+ });
117
+ //# sourceMappingURL=cleanup.js.map
@@ -0,0 +1,3 @@
1
+ export { An5ORM, an5Orm, default } from "./an5Orm";
2
+ export type { Middleware, MiddlewareParams } from "./an5Orm";
3
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
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.default = exports.an5Orm = exports.An5ORM = void 0;
7
+ var an5Orm_1 = require("./an5Orm");
8
+ Object.defineProperty(exports, "An5ORM", { enumerable: true, get: function () { return an5Orm_1.An5ORM; } });
9
+ Object.defineProperty(exports, "an5Orm", { enumerable: true, get: function () { return an5Orm_1.an5Orm; } });
10
+ Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(an5Orm_1).default; } });
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,11 @@
1
+ /**
2
+ * an5Orm Migration Tool
3
+ * Compares schema files with database and generates migration SQL.
4
+ *
5
+ * Usage:
6
+ * npx tsx migrate.ts diff # Show differences
7
+ * npx tsx migrate.ts generate # Generate migration file
8
+ * npx tsx migrate.ts status # Show migration status
9
+ */
10
+ import 'dotenv/config';
11
+ //# sourceMappingURL=migrate.d.ts.map
@@ -0,0 +1,298 @@
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
+ /**
7
+ * an5Orm Migration Tool
8
+ * Compares schema files with database and generates migration SQL.
9
+ *
10
+ * Usage:
11
+ * npx tsx migrate.ts diff # Show differences
12
+ * npx tsx migrate.ts generate # Generate migration file
13
+ * npx tsx migrate.ts status # Show migration status
14
+ */
15
+ require("dotenv/config");
16
+ const fs_1 = __importDefault(require("fs"));
17
+ const path_1 = __importDefault(require("path"));
18
+ const an5_adapters_1 = require("an5-adapters");
19
+ const rootDir = path_1.default.join(__dirname, '../');
20
+ let config = {};
21
+ try {
22
+ const configPath = path_1.default.join(rootDir, 'an5Orm.config.js');
23
+ if (fs_1.default.existsSync(configPath)) {
24
+ config = require(configPath);
25
+ }
26
+ }
27
+ catch { /* ignore */ }
28
+ const schemaDir = path_1.default.resolve(rootDir, config.schemaDir || 'an5Schema');
29
+ const migrationsDir = path_1.default.resolve(rootDir, 'migrations');
30
+ let _adapter = null;
31
+ async function getDb() {
32
+ if (!_adapter) {
33
+ _adapter = new an5_adapters_1.An5Adapter({ connectionString: process.env.DATABASE_URL });
34
+ await _adapter.$connect();
35
+ }
36
+ return _adapter;
37
+ }
38
+ // โ”€โ”€โ”€ Supported SQL Server Types โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
39
+ const AN5_TYPES = new Set([
40
+ 'NVARCHAR', 'VARCHAR', 'CHAR', 'NCHAR', 'TEXT', 'NTEXT', 'XML',
41
+ 'INT', 'SMALLINT', 'TINYINT', 'BIGINT', 'FLOAT', 'REAL', 'DECIMAL', 'NUMERIC',
42
+ 'MONEY', 'SMALLMONEY', 'BIT',
43
+ 'DATETIME', 'DATETIME2', 'SMALLDATETIME', 'DATE', 'TIME', 'DATETIMEOFFSET',
44
+ 'VARBINARY', 'BINARY', 'IMAGE',
45
+ 'UNIQUEIDENTIFIER', 'SQL_VARIANT', 'ROWVERSION',
46
+ 'HIERARCHYID', 'GEOGRAPHY', 'GEOMETRY', 'VECTOR',
47
+ ]);
48
+ function parseSqlType(raw) {
49
+ const match = raw.match(/^(\w+)/);
50
+ return match ? match[1].toUpperCase() : raw.toUpperCase();
51
+ }
52
+ function parseSchema() {
53
+ const models = [];
54
+ if (!fs_1.default.existsSync(schemaDir))
55
+ return models;
56
+ const files = fs_1.default.readdirSync(schemaDir).filter(f => f.endsWith('.an5'));
57
+ let text = '';
58
+ for (const file of files) {
59
+ text += fs_1.default.readFileSync(path_1.default.join(schemaDir, file), 'utf8') + '\n';
60
+ }
61
+ const lines = text.split('\n');
62
+ let current = null;
63
+ for (let line of lines) {
64
+ line = line.trim();
65
+ if (!line || line.startsWith('//'))
66
+ continue;
67
+ const modelMatch = line.match(/^model\s+(\w+)\s*\{/);
68
+ if (modelMatch) {
69
+ current = {
70
+ name: modelMatch[1],
71
+ tableName: modelMatch[1].toLowerCase() + 's',
72
+ fields: [],
73
+ compoundUniques: [],
74
+ indexes: [],
75
+ };
76
+ models.push(current);
77
+ continue;
78
+ }
79
+ if (line === '}') {
80
+ current = null;
81
+ continue;
82
+ }
83
+ if (!current)
84
+ continue;
85
+ if (line.startsWith('@@map')) {
86
+ const m = line.match(/@@map\("(.+)"\)/);
87
+ if (m)
88
+ current.tableName = m[1];
89
+ continue;
90
+ }
91
+ if (line.startsWith('@@unique')) {
92
+ const m = line.match(/@@unique\(\[([\w,\s]+)\]\)/);
93
+ if (m)
94
+ current.compoundUniques.push(m[1].split(',').map(f => f.trim()));
95
+ continue;
96
+ }
97
+ if (line.startsWith('@@index')) {
98
+ const m = line.match(/@@index\(\[([\w,\s]+)\]\)/);
99
+ if (m)
100
+ current.indexes.push(m[1].split(',').map(f => f.trim()));
101
+ continue;
102
+ }
103
+ if (line.startsWith('@@'))
104
+ continue;
105
+ const parts = line.split(/\s+/);
106
+ const fieldName = parts[0];
107
+ const fieldType = parts[1];
108
+ if (!fieldName || !fieldType)
109
+ continue;
110
+ // Parse SQL Server type directly
111
+ const cleanType = fieldType.replace('[]', '').replace('?', '');
112
+ const sqlBase = parseSqlType(cleanType);
113
+ // Skip if not a known SQL Server type (might be a relation)
114
+ if (!AN5_TYPES.has(sqlBase))
115
+ continue;
116
+ current.fields.push({
117
+ name: fieldName,
118
+ sqlType: cleanType.toUpperCase(),
119
+ isOptional: fieldType.endsWith('?'),
120
+ isId: line.includes('@id'),
121
+ isUnique: line.includes('@unique'),
122
+ defaultValue: line.match(/@default\((.*)\)/)?.[1],
123
+ });
124
+ }
125
+ return models;
126
+ }
127
+ async function introspectTable(tableName) {
128
+ return (await getDb()).$queryRawUnsafe(`
129
+ SELECT
130
+ c.name AS columnName,
131
+ ty.name AS dataType,
132
+ c.is_nullable AS isNullable,
133
+ pk.is_primary_key AS isPrimaryKey,
134
+ c.is_identity AS isIdentity,
135
+ d.definition AS defaultValue
136
+ FROM sys.columns c
137
+ JOIN sys.types ty ON c.user_type_id = ty.user_type_id
138
+ LEFT JOIN (
139
+ SELECT ic.object_id, ic.column_id, i.is_primary_key
140
+ FROM sys.index_columns ic
141
+ JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
142
+ WHERE i.is_primary_key = 1
143
+ ) pk ON c.object_id = pk.object_id AND c.column_id = pk.column_id
144
+ LEFT JOIN sys.default_constraints d ON c.default_object_id = d.object_id
145
+ WHERE c.object_id = OBJECT_ID(@p_0)
146
+ ORDER BY c.column_id
147
+ `, tableName);
148
+ }
149
+ async function getExistingTables() {
150
+ const rows = await (await getDb()).$queryRawUnsafe(`
151
+ SELECT name FROM sys.tables WHERE is_ms_shipped = 0 ORDER BY name
152
+ `);
153
+ return rows.map(r => r.name);
154
+ }
155
+ function generateDiff(schemaModels, dbTables) {
156
+ const ops = [];
157
+ const schemaTableNames = new Set(schemaModels.map(m => m.tableName));
158
+ // New tables
159
+ for (const model of schemaModels) {
160
+ if (!dbTables.includes(model.tableName)) {
161
+ const colDefs = model.fields.map(f => {
162
+ let def = `[${f.name}] ${f.sqlType}`;
163
+ if (f.isId)
164
+ def += ' PRIMARY KEY';
165
+ if (f.defaultValue)
166
+ def += ` ${mapDefault(f.defaultValue)}`;
167
+ if (!f.isOptional && !f.defaultValue && !f.isId)
168
+ def += ' NOT NULL';
169
+ if (f.isUnique && !f.isId)
170
+ def += ' UNIQUE';
171
+ return def;
172
+ });
173
+ const sql = `CREATE TABLE [${model.tableName}] (\n ${colDefs.join(',\n ')}\n)`;
174
+ ops.push({ type: 'CREATE_TABLE', table: model.tableName, sql });
175
+ }
176
+ }
177
+ // Dropped tables
178
+ for (const tableName of dbTables) {
179
+ if (!schemaTableNames.has(tableName)) {
180
+ ops.push({
181
+ type: 'DROP_COLUMN',
182
+ table: tableName,
183
+ details: `Table not in schema - consider dropping`,
184
+ sql: `-- DROP TABLE [${tableName}]`,
185
+ });
186
+ }
187
+ }
188
+ return ops;
189
+ }
190
+ function mapDefault(val) {
191
+ if (val === 'uuid()')
192
+ return 'DEFAULT NEWID()';
193
+ if (val === 'cuid()')
194
+ return 'DEFAULT NEWID()';
195
+ if (val === 'now()')
196
+ return 'DEFAULT CURRENT_TIMESTAMP';
197
+ if (val === 'autoincrement()')
198
+ return 'IDENTITY(1,1)';
199
+ if (val === 'true')
200
+ return 'DEFAULT 1';
201
+ if (val === 'false')
202
+ return 'DEFAULT 0';
203
+ if (/^".*"$/.test(val))
204
+ return `DEFAULT '${val.slice(1, -1)}'`;
205
+ return `DEFAULT ${val}`;
206
+ }
207
+ // โ”€โ”€โ”€ Commands โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
208
+ async function cmdDiff() {
209
+ console.log('\n๐Ÿ” Comparing schema with database...\n');
210
+ const schemaModels = parseSchema();
211
+ const dbTables = await getExistingTables();
212
+ console.log(`Schema: ${schemaModels.length} models`);
213
+ console.log(`Database: ${dbTables.length} tables\n`);
214
+ const ops = generateDiff(schemaModels, dbTables);
215
+ if (ops.length === 0) {
216
+ console.log('โœ… Schema is in sync with database.');
217
+ return;
218
+ }
219
+ console.log(`Found ${ops.length} difference(s):\n`);
220
+ for (const op of ops) {
221
+ console.log(` ${op.type}: ${op.table}`);
222
+ if (op.details)
223
+ console.log(` ${op.details}`);
224
+ if (op.sql)
225
+ console.log(` SQL: ${op.sql}`);
226
+ }
227
+ }
228
+ async function cmdGenerate() {
229
+ console.log('\n๐Ÿ“ Generating migration file...\n');
230
+ const schemaModels = parseSchema();
231
+ const dbTables = await getExistingTables();
232
+ const ops = generateDiff(schemaModels, dbTables);
233
+ if (ops.length === 0) {
234
+ console.log('No migrations needed.');
235
+ return;
236
+ }
237
+ if (!fs_1.default.existsSync(migrationsDir)) {
238
+ fs_1.default.mkdirSync(migrationsDir, { recursive: true });
239
+ }
240
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
241
+ const filename = `${timestamp}_migration.sql`;
242
+ const filepath = path_1.default.join(migrationsDir, filename);
243
+ const lines = [`-- Migration: ${timestamp}`, `-- Generated by an5Orm migrate`, ''];
244
+ for (const op of ops) {
245
+ lines.push(`-- ${op.type}: ${op.table}`);
246
+ if (op.sql)
247
+ lines.push(op.sql);
248
+ lines.push('');
249
+ }
250
+ fs_1.default.writeFileSync(filepath, lines.join('\n'));
251
+ console.log(`โœ… Migration written to: ${filepath}`);
252
+ }
253
+ async function cmdStatus() {
254
+ console.log('\n๐Ÿ“Š Migration Status\n');
255
+ const schemaModels = parseSchema();
256
+ const dbTables = await getExistingTables();
257
+ console.log('Schema Models:');
258
+ for (const model of schemaModels) {
259
+ const exists = dbTables.includes(model.tableName);
260
+ const icon = exists ? 'โœ…' : 'โš ๏ธ';
261
+ console.log(` ${icon} ${model.name} โ†’ ${model.tableName} (${model.fields.length} fields)`);
262
+ }
263
+ console.log('\nDatabase Tables:');
264
+ for (const table of dbTables) {
265
+ const inSchema = schemaModels.some(m => m.tableName === table);
266
+ const icon = inSchema ? 'โœ…' : 'โš ๏ธ';
267
+ console.log(` ${icon} ${table}`);
268
+ }
269
+ if (!fs_1.default.existsSync(migrationsDir)) {
270
+ fs_1.default.mkdirSync(migrationsDir, { recursive: true });
271
+ }
272
+ const migrations = fs_1.default.readdirSync(migrationsDir).filter(f => f.endsWith('.sql'));
273
+ console.log(`\nMigration files: ${migrations.length}`);
274
+ }
275
+ // โ”€โ”€โ”€ Main โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
276
+ async function main() {
277
+ const command = process.argv[2] || 'diff';
278
+ switch (command) {
279
+ case 'diff':
280
+ await cmdDiff();
281
+ break;
282
+ case 'generate':
283
+ await cmdGenerate();
284
+ break;
285
+ case 'status':
286
+ await cmdStatus();
287
+ break;
288
+ default:
289
+ console.log('Usage: npx tsx migrate.ts [diff|generate|status]');
290
+ process.exit(1);
291
+ }
292
+ process.exit(0);
293
+ }
294
+ main().catch((err) => {
295
+ console.error('โŒ Migration failed:', err);
296
+ process.exit(1);
297
+ });
298
+ //# sourceMappingURL=migrate.js.map
package/dist/pull.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import "dotenv/config";
2
+ //# sourceMappingURL=pull.d.ts.map