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