@an5/orm 1.0.1 → 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.
Files changed (3) hide show
  1. package/README.md +24 -15
  2. package/dist/push.js +94 -23
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # an5Orm
1
+ # @an5/orm
2
2
 
3
3
  SQL Server ORM. Proxy client. CRUD. Vector search. Middleware. Raw queries. Transactions.
4
4
 
@@ -13,7 +13,13 @@ SQL Server ORM. Proxy client. CRUD. Vector search. Middleware. Raw queries. Tran
13
13
  - **Transactions.** `$transaction`. Rollback support.
14
14
  - **Schema Generator.** Parse `.an5` files. Generate TypeScript/Python/.NET code.
15
15
 
16
- ## Quick Start
16
+ ## Quick Start
17
+
18
+ ### Installation
19
+
20
+ ```bash
21
+ npm install @an5/orm
22
+ ```
17
23
 
18
24
  ### Configuration
19
25
 
@@ -25,17 +31,20 @@ cp .env.example .env
25
31
  ### Development Commands
26
32
 
27
33
  ```bash
28
- # Generate code from schema
29
- npm run generate
30
-
31
- # Push schema to database
32
- npm run db:push
33
-
34
- # Pull schema from database
35
- npm run db:pull
36
-
37
- # Seed database
38
- npm run db:seed
34
+ # Generate code from schema
35
+ npx an5 generate
36
+
37
+ # Push schema to database
38
+ npx an5 db:push
39
+
40
+ # Pull schema from database
41
+ npx an5 db:pull
42
+
43
+ # Seed database
44
+ npx an5 db:seed
45
+
46
+ # Compare schema with database
47
+ npx an5 db:migrate diff
39
48
 
40
49
  # Run tests
41
50
  npm test
@@ -44,7 +53,7 @@ npm test
44
53
  ## Usage
45
54
 
46
55
  ```typescript
47
- import { An5ORM } from './an5Orm';
56
+ import { An5ORM } from '@an5/orm';
48
57
 
49
58
  const db = new An5ORM();
50
59
 
@@ -135,4 +144,4 @@ npm test
135
144
 
136
145
  ## License
137
146
 
138
- MIT
147
+ MIT
package/dist/push.js CHANGED
@@ -6,7 +6,7 @@ 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");
9
+ const adapters_1 = require("@an5/adapters");
10
10
  const rootDir = process.cwd();
11
11
  let config = {};
12
12
  try {
@@ -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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@an5/orm",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
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",
@@ -35,7 +35,7 @@
35
35
  "test:generator": "node test/generator.test.js"
36
36
  },
37
37
  "dependencies": {
38
- "@an5/adapters": "^0.1.0",
38
+ "@an5/adapters": "^0.1.2",
39
39
  "bcryptjs": "^2.4.3",
40
40
  "dotenv": "^16.4.5",
41
41
  "mssql": "^12.5.5"