@an5/orm 1.0.7 → 1.0.9
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 +97 -83
- package/dist/an5Metadata.d.ts +10 -0
- package/dist/an5Metadata.js +93 -0
- package/dist/an5Orm.d.ts +95 -3
- package/dist/an5Orm.js +808 -512
- 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 +1 -0
- package/dist/generator/src/code-generator.js +44 -20
- package/dist/generator/src/dotnet-generator.d.ts +2 -0
- package/dist/generator/src/dotnet-generator.js +332 -6
- package/dist/generator/src/index.js +18 -2
- package/dist/generator/src/metadata-generator.d.ts +4 -1
- package/dist/generator/src/metadata-generator.js +22 -8
- package/dist/generator/src/python-generator.d.ts +10 -0
- package/dist/generator/src/python-generator.js +242 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +23 -8
- 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/migration-core.d.ts +87 -0
- package/dist/migration-core.js +692 -0
- package/dist/sql-utils.d.ts +31 -0
- package/dist/sql-utils.js +290 -0
- package/generator/src/code-generator.ts +45 -20
- package/generator/src/dotnet-generator.ts +344 -6
- package/generator/src/golang-generator.ts +1242 -0
- package/generator/src/index.ts +8 -1
- package/generator/src/metadata-generator.ts +29 -12
- package/generator/src/python-generator.ts +314 -4
- package/package.json +12 -10
- 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/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