@an5/orm 1.0.5 → 1.0.6

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 CHANGED
@@ -1,18 +1,18 @@
1
1
  # @an5/orm
2
-
3
- SQL Server ORM. Proxy client. CRUD. Vector search. Middleware. Raw queries. Transactions.
4
-
5
- ## Features
6
-
7
- - **Proxy Client.** Model access. `db.modelName` syntax.
8
- - **CRUD.** `findMany`, `findFirst`, `findUnique`, `create`, `update`, `delete`, `upsert`.
9
- - **Advanced Queries.** OR/AND, nested relations, aggregates, `groupBy`.
10
- - **Vector Search.** Native SQL Server `VECTOR_DISTANCE`. In-memory fallback.
11
- - **Middleware.** Hook ORM operations: logging, auth, validation.
12
- - **Raw Queries.** `$queryRaw`, `$executeRaw`. Auto `NOLOCK`.
13
- - **Transactions.** `$transaction`. Rollback support.
14
- - **Schema Generator.** Parse `.an5` files. Generate TypeScript/Python/.NET code.
15
-
2
+
3
+ SQL Server ORM. Proxy client. CRUD. Vector search. Middleware. Raw queries. Transactions.
4
+
5
+ ## Features
6
+
7
+ - **Proxy Client.** Model access. `db.modelName` syntax.
8
+ - **CRUD.** `findMany`, `findFirst`, `findUnique`, `create`, `update`, `delete`, `upsert`.
9
+ - **Advanced Queries.** OR/AND, nested relations, aggregates, `groupBy`.
10
+ - **Vector Search.** Native SQL Server `VECTOR_DISTANCE`. In-memory fallback.
11
+ - **Middleware.** Hook ORM operations: logging, auth, validation.
12
+ - **Raw Queries.** `$queryRaw`, `$executeRaw`. Auto `NOLOCK`.
13
+ - **Transactions.** `$transaction`. Rollback support.
14
+ - **Schema Generator.** Parse `.an5` files. Generate TypeScript/Python/.NET code.
15
+
16
16
  ## Quick Start
17
17
 
18
18
  ### Installation
@@ -20,17 +20,17 @@ SQL Server ORM. Proxy client. CRUD. Vector search. Middleware. Raw queries. Tran
20
20
  ```bash
21
21
  npm install @an5/orm
22
22
  ```
23
-
24
- ### Configuration
25
-
26
- ```bash
27
- cp .env.example .env
28
- # Edit .env. Set DATABASE_URL.
29
- ```
30
-
31
- ### Development Commands
32
-
33
- ```bash
23
+
24
+ ### Configuration
25
+
26
+ ```bash
27
+ cp .env.example .env
28
+ # Edit .env. Set DATABASE_URL.
29
+ ```
30
+
31
+ ### Development Commands
32
+
33
+ ```bash
34
34
  # Generate code from schema
35
35
  npx an5 generate
36
36
 
@@ -45,103 +45,103 @@ npx an5 db:seed
45
45
 
46
46
  # Compare schema with database
47
47
  npx an5 db:migrate diff
48
-
49
- # Run tests
50
- npm test
51
- ```
52
-
53
- ## Usage
54
-
55
- ```typescript
48
+
49
+ # Run tests
50
+ npm test
51
+ ```
52
+
53
+ ## Usage
54
+
55
+ ```typescript
56
56
  import { An5ORM } from '@an5/orm';
57
-
58
- const db = new An5ORM();
59
-
60
- // CRUD Operations
61
- const users = await db.user.findMany({
62
- where: { email: { contains: '@example.com' } },
63
- orderBy: { createdAt: 'desc' },
64
- take: 10,
65
- });
66
-
67
- const user = await db.user.create({
68
- data: { email: 'john@example.com', name: 'John' },
69
- });
70
-
71
- // Relations
72
- const orders = await db.user.findMany({
73
- include: { orders: true },
74
- });
75
-
76
- // Vector Search
77
- const similar = await db.document.vectorSearch({
78
- vector: [0.1, 0.2, 0.3],
79
- take: 5,
80
- distanceMetric: 'cosine',
81
- });
82
-
83
- // Transactions
84
- await db.$transaction(async (tx) => {
85
- const user = await tx.user.create({ data: { email: 'jane@example.com' } });
86
- await tx.order.create({ data: { userId: user.id, total: 100 } });
87
- });
88
-
89
- // Raw Queries
90
- const results = await db.$queryRaw`SELECT * FROM users WHERE id = ${id}`;
91
- ```
92
-
93
- ## Schema Definition
94
-
95
- Schema files: `.an5`. Path: `an5Schema/`. SQL Server types.
96
-
97
- ```an5
98
- model User {
99
- id NVARCHAR(1000) @id @default(uuid())
100
- email NVARCHAR(255) @unique
101
- name NVARCHAR(255)?
102
- createdAt DATETIME2 @default(now())
103
- orders Order[]
104
-
105
- @@map("users")
106
- }
107
-
108
- model Order {
109
- id NVARCHAR(1000) @id @default(uuid())
110
- userId NVARCHAR(1000)
111
- total INT @default(0)
112
- user User @relation(fields: [userId], references: [id])
113
-
114
- @@map("orders")
115
- }
116
- ```
117
-
118
- ## Supported SQL Server Types
119
-
120
- Type mapping: `.an5` to TypeScript.
121
-
122
- | Schema Type | TypeScript |
123
- |-------------|------------|
124
- | `NVARCHAR(n)`, `VARCHAR(n)`, `CHAR(n)`, `TEXT` | `string` |
125
- | `INT`, `SMALLINT`, `TINYINT`, `FLOAT`, `REAL`, `DECIMAL`, `NUMERIC` | `number` |
126
- | `BIGINT` | `number \| bigint` |
127
- | `BIT` | `boolean` |
128
- | `DATETIME`, `DATETIME2`, `DATE`, `TIME` | `Date` |
129
- | `UNIQUEIDENTIFIER` | `string` |
130
- | `VARBINARY`, `BINARY`, `IMAGE` | `Buffer` |
131
-
132
- ## Testing
133
-
134
- ```bash
135
- # Unit tests
136
- node test/unit.test.js
137
-
138
- # Generator tests
139
- node test/generator.test.js
140
-
141
- # Smoke test
142
- npm test
143
- ```
144
-
145
- ## License
146
-
57
+
58
+ const db = new An5ORM();
59
+
60
+ // CRUD Operations
61
+ const users = await db.user.findMany({
62
+ where: { email: { contains: '@example.com' } },
63
+ orderBy: { createdAt: 'desc' },
64
+ take: 10,
65
+ });
66
+
67
+ const user = await db.user.create({
68
+ data: { email: 'john@example.com', name: 'John' },
69
+ });
70
+
71
+ // Relations
72
+ const orders = await db.user.findMany({
73
+ include: { orders: true },
74
+ });
75
+
76
+ // Vector Search
77
+ const similar = await db.document.vectorSearch({
78
+ vector: [0.1, 0.2, 0.3],
79
+ take: 5,
80
+ distanceMetric: 'cosine',
81
+ });
82
+
83
+ // Transactions
84
+ await db.$transaction(async (tx) => {
85
+ const user = await tx.user.create({ data: { email: 'jane@example.com' } });
86
+ await tx.order.create({ data: { userId: user.id, total: 100 } });
87
+ });
88
+
89
+ // Raw Queries
90
+ const results = await db.$queryRaw`SELECT * FROM users WHERE id = ${id}`;
91
+ ```
92
+
93
+ ## Schema Definition
94
+
95
+ Schema files: `.an5`. Path: `an5Schema/`. SQL Server types.
96
+
97
+ ```an5
98
+ model User {
99
+ id NVARCHAR(1000) @id @default(uuid())
100
+ email NVARCHAR(255) @unique
101
+ name NVARCHAR(255)?
102
+ createdAt DATETIME2 @default(now())
103
+ orders Order[]
104
+
105
+ @@map("users")
106
+ }
107
+
108
+ model Order {
109
+ id NVARCHAR(1000) @id @default(uuid())
110
+ userId NVARCHAR(1000)
111
+ total INT @default(0)
112
+ user User @relation(fields: [userId], references: [id])
113
+
114
+ @@map("orders")
115
+ }
116
+ ```
117
+
118
+ ## Supported SQL Server Types
119
+
120
+ Type mapping: `.an5` to TypeScript.
121
+
122
+ | Schema Type | TypeScript |
123
+ |-------------|------------|
124
+ | `NVARCHAR(n)`, `VARCHAR(n)`, `CHAR(n)`, `TEXT` | `string` |
125
+ | `INT`, `SMALLINT`, `TINYINT`, `FLOAT`, `REAL`, `DECIMAL`, `NUMERIC` | `number` |
126
+ | `BIGINT` | `number \| bigint` |
127
+ | `BIT` | `boolean` |
128
+ | `DATETIME`, `DATETIME2`, `DATE`, `TIME` | `Date` |
129
+ | `UNIQUEIDENTIFIER` | `string` |
130
+ | `VARBINARY`, `BINARY`, `IMAGE` | `Buffer` |
131
+
132
+ ## Testing
133
+
134
+ ```bash
135
+ # Unit tests
136
+ node test/unit.test.js
137
+
138
+ # Generator tests
139
+ node test/generator.test.js
140
+
141
+ # Smoke test
142
+ npm test
143
+ ```
144
+
145
+ ## License
146
+
147
147
  MIT
package/dist/an5Orm.js CHANGED
@@ -238,11 +238,11 @@ async function resolveIncludes(modelName, rows, include, executor) {
238
238
  rows.forEach(r => { r._count = { ...r._count, [countField]: 0 }; });
239
239
  continue;
240
240
  }
241
- const sqlText = `
242
- SELECT ${rel.foreignKey} as parentId, COUNT(*) as count
243
- FROM ${relTable} WITH (NOLOCK)
244
- WHERE ${rel.foreignKey} IN (${localKeys.map((_, i) => `@lk_${i}`).join(", ")})
245
- GROUP BY ${rel.foreignKey}
241
+ const sqlText = `
242
+ SELECT ${rel.foreignKey} as parentId, COUNT(*) as count
243
+ FROM ${relTable} WITH (NOLOCK)
244
+ WHERE ${rel.foreignKey} IN (${localKeys.map((_, i) => `@lk_${i}`).join(", ")})
245
+ GROUP BY ${rel.foreignKey}
246
246
  `;
247
247
  const countParams = {};
248
248
  localKeys.forEach((lk, i) => { countParams[`lk_${i}`] = lk; });
@@ -1065,15 +1065,15 @@ class TableClient {
1065
1065
  }
1066
1066
  return `target.[${k}] = @upw_${k}`;
1067
1067
  }).join(" AND ");
1068
- const sqlText = `
1069
- MERGE INTO ${this.tableName} WITH (HOLDLOCK) AS target
1070
- USING (SELECT ${sourceSelect}) AS source
1071
- ON (${onClause})
1072
- WHEN MATCHED THEN
1073
- UPDATE SET ${updateSets}
1074
- WHEN NOT MATCHED THEN
1075
- INSERT (${insertCols}) VALUES (${insertVals})
1076
- OUTPUT inserted.*;
1068
+ const sqlText = `
1069
+ MERGE INTO ${this.tableName} WITH (HOLDLOCK) AS target
1070
+ USING (SELECT ${sourceSelect}) AS source
1071
+ ON (${onClause})
1072
+ WHEN MATCHED THEN
1073
+ UPDATE SET ${updateSets}
1074
+ WHEN NOT MATCHED THEN
1075
+ INSERT (${insertCols}) VALUES (${insertVals})
1076
+ OUTPUT inserted.*;
1077
1077
  `;
1078
1078
  try {
1079
1079
  const rows = await this.executor(sqlText, p);
package/dist/cleanup.js CHANGED
@@ -10,7 +10,10 @@ const path_1 = __importDefault(require("path"));
10
10
  const rootDir = process.cwd();
11
11
  let config = {};
12
12
  try {
13
- const configPath = path_1.default.join(rootDir, "an5Orm.config.js");
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
+ }
14
17
  if (fs_1.default.existsSync(configPath)) {
15
18
  config = require(configPath);
16
19
  }
@@ -68,8 +71,8 @@ async function cleanup() {
68
71
  // Also include the capitalization variations from an5Metadata if we can,
69
72
  // but schema.an5 is the source of truth.
70
73
  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
74
+ const dbTables = await (await getDb()).$queryRawUnsafe(`
75
+ SELECT name FROM sys.tables WHERE is_ms_shipped = 0
73
76
  `);
74
77
  const tablesToDrop = dbTables
75
78
  .map(t => t.name)
@@ -92,10 +95,10 @@ async function cleanup() {
92
95
  console.error(`❌ Failed to drop [${table}]: ${err.message}`);
93
96
  if (err.message.includes("FOREIGN KEY constraint")) {
94
97
  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
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
99
102
  `, table);
100
103
  for (const c of constraints) {
101
104
  console.log(`Dropping constraint [${c.constraint_name}] on [${c.table_name}]...`);
package/dist/cli.js CHANGED
File without changes
@@ -21,45 +21,45 @@ class CodeGenerator {
21
21
  this.generateIndexTs(models);
22
22
  }
23
23
  generateBaseTs() {
24
- const baseTs = `
25
- export namespace An5 {
26
- export class An5ClientKnownRequestError extends Error {
27
- code!: string;
28
- meta?: any;
29
- constructor(message: string, { code, clientVersion }: { code: string; clientVersion: string }) {
30
- super(message);
31
- this.code = code;
32
- }
33
- }
34
- export type SortOrder = 'asc' | 'desc';
35
- export type StringFilter = { equals?: string; in?: string[]; notIn?: string[]; lt?: string; lte?: string; gt?: string; gte?: string; contains?: string; startsWith?: string; endsWith?: string; not?: string | StringFilter; };
36
- export type StringNullableFilter = { equals?: string | null; in?: (string | null)[]; notIn?: (string | null)[]; lt?: string; lte?: string; gt?: string; gte?: string; contains?: string; startsWith?: string; endsWith?: string; not?: string | StringNullableFilter | null; };
37
- export type NumberFilter = { equals?: number; in?: number[]; notIn?: number[]; lt?: number; lte?: number; gt?: number; gte?: number; not?: number | NumberFilter; };
38
- export type IntFieldUpdateOperationsInput = { set?: number; increment?: number; decrement?: number; multiply?: number; divide?: number; };
39
- export type FloatFieldUpdateOperationsInput = { set?: number; increment?: number; decrement?: number; multiply?: number; divide?: number; };
40
- export type NumberNullableFilter = { equals?: number | null; in?: (number | null)[]; notIn?: (number | null)[]; lt?: number; lte?: number; gt?: number; gte?: number; not?: number | NumberNullableFilter | null; };
41
- export type BooleanFilter = { equals?: boolean; not?: boolean | BooleanFilter; };
42
- export type BooleanNullableFilter = { equals?: boolean | null; not?: boolean | BooleanNullableFilter | null; };
43
- export type DateTimeFilter = { equals?: Date; in?: Date[]; notIn?: Date[]; lt?: Date; lte?: Date; gt?: Date; gte?: Date; not?: Date | DateTimeFilter; };
44
- export type DateTimeNullableFilter = { equals?: Date | null; in?: (Date | null)[]; notIn?: (Date | null)[]; lt?: Date; lte?: Date; gt?: Date; gte?: Date; not?: Date | DateTimeNullableFilter | null; };
45
- }
46
-
47
- export interface TableClient<T, WhereInput = any, Select = any, Include = any, CreateInput = any, UpdateInput = any, FindManyArgs = any, FindFirstArgs = any, FindUniqueArgs = any, CreateArgs = any, UpdateArgs = any, UpsertArgs = any, DeleteArgs = any> {
48
- findMany(args?: FindManyArgs): Promise<T[]>;
49
- vectorSearch(args: { vector: number[]; take?: number; where?: WhereInput; include?: Include; vectorField?: string; distanceMetric?: 'cosine' | 'euclidean' | 'dot'; }): Promise<(T & { distance: number })[]>;
50
- findFirst(args?: FindFirstArgs): Promise<T | null>;
51
- findUnique(args?: FindUniqueArgs): Promise<T | null>;
52
- count(args?: { where?: WhereInput; }): Promise<number>;
53
- create(args: CreateArgs): Promise<T>;
54
- createMany(args: { data: CreateInput[]; skipDuplicates?: boolean; }): Promise<{ count: number }>;
55
- update(args: UpdateArgs): Promise<T>;
56
- updateMany(args: { where?: WhereInput; data: UpdateInput; }): Promise<{ count: number }>;
57
- delete(args: DeleteArgs): Promise<T>;
58
- deleteMany(args?: { where?: WhereInput; }): Promise<{ count: number }>;
59
- aggregate(args: any): Promise<any>;
60
- groupBy(args: any): Promise<any[]>;
61
- upsert(args: UpsertArgs): Promise<T>;
62
- }
24
+ const baseTs = `
25
+ export namespace An5 {
26
+ export class An5ClientKnownRequestError extends Error {
27
+ code!: string;
28
+ meta?: any;
29
+ constructor(message: string, { code, clientVersion }: { code: string; clientVersion: string }) {
30
+ super(message);
31
+ this.code = code;
32
+ }
33
+ }
34
+ export type SortOrder = 'asc' | 'desc';
35
+ export type StringFilter = { equals?: string; in?: string[]; notIn?: string[]; lt?: string; lte?: string; gt?: string; gte?: string; contains?: string; startsWith?: string; endsWith?: string; not?: string | StringFilter; };
36
+ export type StringNullableFilter = { equals?: string | null; in?: (string | null)[]; notIn?: (string | null)[]; lt?: string; lte?: string; gt?: string; gte?: string; contains?: string; startsWith?: string; endsWith?: string; not?: string | StringNullableFilter | null; };
37
+ export type NumberFilter = { equals?: number; in?: number[]; notIn?: number[]; lt?: number; lte?: number; gt?: number; gte?: number; not?: number | NumberFilter; };
38
+ export type IntFieldUpdateOperationsInput = { set?: number; increment?: number; decrement?: number; multiply?: number; divide?: number; };
39
+ export type FloatFieldUpdateOperationsInput = { set?: number; increment?: number; decrement?: number; multiply?: number; divide?: number; };
40
+ export type NumberNullableFilter = { equals?: number | null; in?: (number | null)[]; notIn?: (number | null)[]; lt?: number; lte?: number; gt?: number; gte?: number; not?: number | NumberNullableFilter | null; };
41
+ export type BooleanFilter = { equals?: boolean; not?: boolean | BooleanFilter; };
42
+ export type BooleanNullableFilter = { equals?: boolean | null; not?: boolean | BooleanNullableFilter | null; };
43
+ export type DateTimeFilter = { equals?: Date; in?: Date[]; notIn?: Date[]; lt?: Date; lte?: Date; gt?: Date; gte?: Date; not?: Date | DateTimeFilter; };
44
+ export type DateTimeNullableFilter = { equals?: Date | null; in?: (Date | null)[]; notIn?: (Date | null)[]; lt?: Date; lte?: Date; gt?: Date; gte?: Date; not?: Date | DateTimeNullableFilter | null; };
45
+ }
46
+
47
+ export interface TableClient<T, WhereInput = any, Select = any, Include = any, CreateInput = any, UpdateInput = any, FindManyArgs = any, FindFirstArgs = any, FindUniqueArgs = any, CreateArgs = any, UpdateArgs = any, UpsertArgs = any, DeleteArgs = any> {
48
+ findMany(args?: FindManyArgs): Promise<T[]>;
49
+ vectorSearch(args: { vector: number[]; take?: number; where?: WhereInput; include?: Include; vectorField?: string; distanceMetric?: 'cosine' | 'euclidean' | 'dot'; }): Promise<(T & { distance: number })[]>;
50
+ findFirst(args?: FindFirstArgs): Promise<T | null>;
51
+ findUnique(args?: FindUniqueArgs): Promise<T | null>;
52
+ count(args?: { where?: WhereInput; }): Promise<number>;
53
+ create(args: CreateArgs): Promise<T>;
54
+ createMany(args: { data: CreateInput[]; skipDuplicates?: boolean; }): Promise<{ count: number }>;
55
+ update(args: UpdateArgs): Promise<T>;
56
+ updateMany(args: { where?: WhereInput; data: UpdateInput; }): Promise<{ count: number }>;
57
+ delete(args: DeleteArgs): Promise<T>;
58
+ deleteMany(args?: { where?: WhereInput; }): Promise<{ count: number }>;
59
+ aggregate(args: any): Promise<any>;
60
+ groupBy(args: any): Promise<any[]>;
61
+ upsert(args: UpsertArgs): Promise<T>;
62
+ }
63
63
  `;
64
64
  fs_1.default.writeFileSync(path_1.default.join(this.outputDir, 'base.ts'), baseTs);
65
65
  }