@an5/orm 1.0.7 → 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.
Files changed (52) hide show
  1. package/README.md +175 -153
  2. package/dist/an5Metadata.d.ts +10 -0
  3. package/dist/an5Metadata.js +29 -0
  4. package/dist/an5Orm.d.ts +4 -1
  5. package/dist/an5Orm.js +214 -409
  6. package/dist/config.d.ts +2 -0
  7. package/dist/config.js +24 -0
  8. package/dist/errors.d.ts +14 -0
  9. package/dist/errors.js +16 -0
  10. package/dist/generator/src/code-generator.d.ts +13 -13
  11. package/dist/generator/src/code-generator.js +225 -225
  12. package/dist/generator/src/dotnet-generator.d.ts +11 -11
  13. package/dist/generator/src/dotnet-generator.js +546 -546
  14. package/dist/generator/src/index.d.ts +1 -1
  15. package/dist/generator/src/index.js +88 -88
  16. package/dist/generator/src/metadata-generator.d.ts +8 -8
  17. package/dist/generator/src/metadata-generator.js +90 -90
  18. package/dist/generator/src/parser.d.ts +11 -11
  19. package/dist/generator/src/parser.js +206 -206
  20. package/dist/generator/src/python-generator.d.ts +9 -9
  21. package/dist/generator/src/python-generator.js +105 -105
  22. package/dist/generator/src/types.d.ts +27 -27
  23. package/dist/generator/src/types.js +2 -2
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.js +5 -1
  26. package/dist/logger.d.ts +53 -0
  27. package/dist/logger.js +117 -0
  28. package/dist/metadata.d.ts +24 -0
  29. package/dist/metadata.js +15 -0
  30. package/dist/scripts/release-cli.d.ts +2 -2
  31. package/dist/scripts/release-cli.js +115 -115
  32. package/dist/sql-utils.d.ts +29 -0
  33. package/dist/sql-utils.js +220 -0
  34. package/generator/src/code-generator.ts +230 -234
  35. package/generator/src/dotnet-generator.ts +544 -544
  36. package/generator/src/index.ts +116 -104
  37. package/generator/src/metadata-generator.ts +101 -99
  38. package/generator/src/parser.ts +202 -202
  39. package/generator/src/python-generator.ts +114 -114
  40. package/generator/src/types.ts +29 -29
  41. package/package.json +2 -6
  42. package/dist/cleanup.d.ts +0 -2
  43. package/dist/cleanup.js +0 -120
  44. package/dist/cli.js +0 -64
  45. package/dist/migrate.d.ts +0 -11
  46. package/dist/migrate.js +0 -301
  47. package/dist/pull.d.ts +0 -2
  48. package/dist/pull.js +0 -339
  49. package/dist/push.d.ts +0 -2
  50. package/dist/push.js +0 -281
  51. package/dist/seed.d.ts +0 -2
  52. package/dist/seed.js +0 -401
package/dist/an5Orm.js CHANGED
@@ -1,19 +1,22 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.an5Orm = exports.An5ORM = void 0;
7
- const mssql_1 = __importDefault(require("mssql"));
8
- const an5_adapters_1 = require("@an5/adapters");
4
+ const adapters_1 = require("@an5/adapters");
9
5
  const crypto_1 = require("crypto");
10
- const logger_1 = require("@an5/lib/logger");
11
- const typescript_1 = require("an5-client/typescript");
12
- const an5Metadata_1 = require("an5-client/typescript/an5Metadata");
6
+ const logger_1 = require("./logger");
7
+ const errors_1 = require("./errors");
8
+ const metadata_1 = require("./metadata");
9
+ const sql_utils_1 = require("./sql-utils");
10
+ function aggSelect(fn, key) {
11
+ return `${fn}(${(0, sql_utils_1.quoteIdentifier)(key)}) as ${aggAlias(fn, key)}`;
12
+ }
13
+ function aggAlias(fn, key) {
14
+ return (0, sql_utils_1.sanitizeParamName)(`${fn}_${key}`);
15
+ }
13
16
  let adapter = null;
14
17
  async function getAdapter() {
15
18
  if (!adapter) {
16
- adapter = new an5_adapters_1.An5Adapter({ connectionString: process.env.DATABASE_URL });
19
+ adapter = new adapters_1.An5Adapter({ connectionString: process.env.DATABASE_URL });
17
20
  await adapter.$connect();
18
21
  }
19
22
  return adapter;
@@ -22,185 +25,6 @@ async function execQuery(queryText, params) {
22
25
  const a = await getAdapter();
23
26
  return a.exec(queryText, params);
24
27
  }
25
- function parseWhere(modelName, where, params, prefix = "") {
26
- if (!where)
27
- return "";
28
- const conditions = [];
29
- const cleanWhere = {};
30
- for (const [key, value] of Object.entries(where)) {
31
- if (key.includes("_") &&
32
- value &&
33
- typeof value === "object" &&
34
- !(value instanceof Date) &&
35
- !value.in &&
36
- !value.contains &&
37
- !value.not &&
38
- !value.gte &&
39
- !value.lte &&
40
- !value.gt &&
41
- !value.lt) {
42
- Object.assign(cleanWhere, value);
43
- }
44
- else {
45
- cleanWhere[key] = value;
46
- }
47
- }
48
- for (const [key, value] of Object.entries(cleanWhere)) {
49
- if (key === "OR" && Array.isArray(value)) {
50
- const orConditions = value.map((subWhere, idx) => parseWhere(modelName, subWhere, params, `${prefix}or_${idx}_`));
51
- const filtered = orConditions.filter(Boolean);
52
- if (filtered.length > 0) {
53
- conditions.push(`(${filtered.join(" OR ")})`);
54
- }
55
- }
56
- else if (key === "AND" && Array.isArray(value)) {
57
- const andConditions = value.map((subWhere, idx) => parseWhere(modelName, subWhere, params, `${prefix}and_${idx}_`));
58
- const filtered = andConditions.filter(Boolean);
59
- if (filtered.length > 0) {
60
- conditions.push(`(${filtered.join(" AND ")})`);
61
- }
62
- }
63
- else {
64
- const modelRelations = an5Metadata_1.relationMap[modelName];
65
- const relation = modelRelations?.[key];
66
- if (relation) {
67
- // Relation subquery
68
- const relationTable = an5Metadata_1.modelToTable[relation.modelName];
69
- const subParams = {};
70
- let subWhere = value;
71
- let op = "some"; // default
72
- if (value && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date)) {
73
- if (value.some) {
74
- subWhere = value.some;
75
- op = "some";
76
- }
77
- else if (value.none) {
78
- subWhere = value.none;
79
- op = "none";
80
- }
81
- else if (value.every) {
82
- subWhere = value.every;
83
- op = "every";
84
- }
85
- }
86
- const subWhereSql = parseWhere(relation.modelName, subWhere, subParams, `${prefix}${key}_`);
87
- Object.assign(params, subParams);
88
- if (subWhereSql) {
89
- if (relation.relationType === "one") {
90
- if (op === "none") {
91
- conditions.push(`${relation.foreignKey} NOT IN (SELECT ${relation.localKey} FROM ${relationTable} WITH (NOLOCK) WHERE ${subWhereSql})`);
92
- }
93
- else {
94
- conditions.push(`${relation.foreignKey} IN (SELECT ${relation.localKey} FROM ${relationTable} WITH (NOLOCK) WHERE ${subWhereSql})`);
95
- }
96
- }
97
- else {
98
- if (op === "none") {
99
- conditions.push(`${relation.localKey} NOT IN (SELECT ${relation.foreignKey} FROM ${relationTable} WITH (NOLOCK) WHERE ${subWhereSql})`);
100
- }
101
- else {
102
- conditions.push(`${relation.localKey} IN (SELECT ${relation.foreignKey} FROM ${relationTable} WITH (NOLOCK) WHERE ${subWhereSql})`);
103
- }
104
- }
105
- }
106
- }
107
- else {
108
- const paramName = `${prefix}${key}`;
109
- if (value && typeof value === "object" && !(value instanceof Date)) {
110
- const ops = Object.entries(value);
111
- for (const [op, opVal] of ops) {
112
- if (op === "in" && Array.isArray(opVal)) {
113
- if (opVal.length === 0) {
114
- conditions.push("1 = 0");
115
- }
116
- else {
117
- const inParams = [];
118
- opVal.forEach((item, idx) => {
119
- const inParamName = `${paramName}_in_${idx}`;
120
- inParams.push(`@${inParamName}`);
121
- params[inParamName] = item;
122
- });
123
- conditions.push(`${key} IN (${inParams.join(", ")})`);
124
- }
125
- }
126
- else if (op === "notIn" && Array.isArray(opVal)) {
127
- if (opVal.length === 0) {
128
- // NOT IN empty list is always true, but let's be safe
129
- conditions.push("1 = 1");
130
- }
131
- else {
132
- const inParams = [];
133
- opVal.forEach((item, idx) => {
134
- const inParamName = `${paramName}_notin_${idx}`;
135
- inParams.push(`@${inParamName}`);
136
- params[inParamName] = item;
137
- });
138
- conditions.push(`${key} NOT IN (${inParams.join(", ")})`);
139
- }
140
- }
141
- else if (op === "contains") {
142
- conditions.push(`${key} LIKE @${paramName}_contains`);
143
- params[`${paramName}_contains`] = `%${opVal}%`;
144
- }
145
- else if (op === "startsWith") {
146
- conditions.push(`${key} LIKE @${paramName}_startsWith`);
147
- params[`${paramName}_startsWith`] = `${opVal}%`;
148
- }
149
- else if (op === "endsWith") {
150
- conditions.push(`${key} LIKE @${paramName}_endsWith`);
151
- params[`${paramName}_endsWith`] = `%${opVal}`;
152
- }
153
- else if (op === "not") {
154
- conditions.push(`${key} <> @${paramName}_not`);
155
- params[`${paramName}_not`] = opVal;
156
- }
157
- else if (op === "gte") {
158
- conditions.push(`${key} >= @${paramName}_gte`);
159
- params[`${paramName}_gte`] = opVal;
160
- }
161
- else if (op === "lte") {
162
- conditions.push(`${key} <= @${paramName}_lte`);
163
- params[`${paramName}_lte`] = opVal;
164
- }
165
- else if (op === "gt") {
166
- conditions.push(`${key} > @${paramName}_gt`);
167
- params[`${paramName}_gt`] = opVal;
168
- }
169
- else if (op === "lt") {
170
- conditions.push(`${key} < @${paramName}_lt`);
171
- params[`${paramName}_lt`] = opVal;
172
- }
173
- }
174
- }
175
- else {
176
- if (value === null) {
177
- conditions.push(`${key} IS NULL`);
178
- }
179
- else {
180
- conditions.push(`${key} = @${paramName}`);
181
- params[paramName] = value;
182
- }
183
- }
184
- }
185
- }
186
- }
187
- return conditions.join(" AND ");
188
- }
189
- function buildOrderBy(orderBy) {
190
- if (!orderBy)
191
- return "";
192
- const orderClauses = [];
193
- const orderByArr = Array.isArray(orderBy) ? orderBy : [orderBy];
194
- for (const orderObj of orderByArr) {
195
- if (orderObj && typeof orderObj === "object") {
196
- for (const [k, dir] of Object.entries(orderObj)) {
197
- const dirStr = typeof dir === "string" ? dir.toUpperCase() : "ASC";
198
- orderClauses.push(`${k} ${dirStr}`);
199
- }
200
- }
201
- }
202
- return orderClauses.length > 0 ? ` ORDER BY ` + orderClauses.join(", ") : "";
203
- }
204
28
  function projectFields(row, select) {
205
29
  if (!row || !select)
206
30
  return row;
@@ -216,10 +40,10 @@ function projectFields(row, select) {
216
40
  return projected;
217
41
  }
218
42
  // Helper to batch-query and resolve relationships
219
- async function resolveIncludes(modelName, rows, include, executor) {
43
+ async function resolveIncludes(modelName, rows, include, executor, metadata) {
220
44
  if (!rows || rows.length === 0 || !include)
221
45
  return;
222
- const modelRelations = an5Metadata_1.relationMap[modelName];
46
+ const modelRelations = metadata.relationMap[modelName];
223
47
  if (!modelRelations)
224
48
  return;
225
49
  for (const [key, value] of Object.entries(include)) {
@@ -232,17 +56,17 @@ async function resolveIncludes(modelName, rows, include, executor) {
232
56
  for (const countField of countFields) {
233
57
  const rel = modelRelations[countField];
234
58
  if (rel) {
235
- const relTable = an5Metadata_1.modelToTable[rel.modelName];
59
+ const relTable = metadata.modelToTable[rel.modelName];
236
60
  const localKeys = rows.map(r => r[rel.localKey]).filter(Boolean);
237
61
  if (localKeys.length === 0) {
238
62
  rows.forEach(r => { r._count = { ...r._count, [countField]: 0 }; });
239
63
  continue;
240
64
  }
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}
65
+ const sqlText = `
66
+ SELECT ${(0, sql_utils_1.quoteIdentifier)(rel.foreignKey)} as parentId, COUNT(*) as count
67
+ FROM ${relTable} WITH (NOLOCK)
68
+ WHERE ${(0, sql_utils_1.quoteIdentifier)(rel.foreignKey)} IN (${localKeys.map((_, i) => `@lk_${i}`).join(", ")})
69
+ GROUP BY ${(0, sql_utils_1.quoteIdentifier)(rel.foreignKey)}
246
70
  `;
247
71
  const countParams = {};
248
72
  localKeys.forEach((lk, i) => { countParams[`lk_${i}`] = lk; });
@@ -258,7 +82,7 @@ async function resolveIncludes(modelName, rows, include, executor) {
258
82
  }
259
83
  continue;
260
84
  }
261
- const relTable = an5Metadata_1.modelToTable[relation.modelName];
85
+ const relTable = metadata.modelToTable[relation.modelName];
262
86
  const isMany = relation.relationType === "many";
263
87
  const matchKey = relation.relationType === "one" ? relation.foreignKey : relation.localKey;
264
88
  const searchKey = relation.relationType === "one" ? relation.localKey : relation.foreignKey;
@@ -271,24 +95,25 @@ async function resolveIncludes(modelName, rows, include, executor) {
271
95
  let relCols = "*";
272
96
  if (value && typeof value === "object" && value.select) {
273
97
  const subSelect = value.select;
274
- const subRelations = an5Metadata_1.relationMap[relation.modelName] || {};
98
+ const subRelations = metadata.relationMap[relation.modelName] || {};
275
99
  const selectedSubCols = Object.keys(subSelect)
276
100
  .filter(k => subSelect[k] && !subRelations[k])
277
- .map(k => `[${k}]`);
101
+ .map(k => (0, sql_utils_1.quoteIdentifier)(k));
278
102
  if (selectedSubCols.length > 0) {
279
- if (!selectedSubCols.includes(`[${searchKey}]`)) {
280
- selectedSubCols.push(`[${searchKey}]`);
103
+ const quotedSearchKey = (0, sql_utils_1.quoteIdentifier)(searchKey);
104
+ if (!selectedSubCols.includes(quotedSearchKey)) {
105
+ selectedSubCols.push(quotedSearchKey);
281
106
  }
282
107
  relCols = selectedSubCols.join(", ");
283
108
  }
284
109
  }
285
- let sqlText = `SELECT ${relCols} FROM ${relTable} WITH (NOLOCK) WHERE ${searchKey} IN (${uniqueKeys.map((_, i) => `@k_${i}`).join(", ")})`;
110
+ let sqlText = `SELECT ${relCols} FROM ${relTable} WITH (NOLOCK) WHERE ${(0, sql_utils_1.quoteIdentifier)(searchKey)} IN (${uniqueKeys.map((_, i) => `@k_${i}`).join(", ")})`;
286
111
  const subParams = {};
287
112
  uniqueKeys.forEach((k, i) => { subParams[`k_${i}`] = k; });
288
113
  if (value && typeof value === "object") {
289
114
  const subArgs = value;
290
115
  if (subArgs.orderBy) {
291
- sqlText += buildOrderBy(subArgs.orderBy);
116
+ sqlText += (0, sql_utils_1.buildOrderBy)(subArgs.orderBy);
292
117
  }
293
118
  }
294
119
  const relatedRows = await executor(sqlText, subParams);
@@ -298,7 +123,7 @@ async function resolveIncludes(modelName, rows, include, executor) {
298
123
  });
299
124
  }
300
125
  if (value && typeof value === "object" && value.include) {
301
- await resolveIncludes(relation.modelName, relatedRows, value.include, executor);
126
+ await resolveIncludes(relation.modelName, relatedRows, value.include, executor, metadata);
302
127
  }
303
128
  const groupMap = new Map();
304
129
  relatedRows.forEach((r) => {
@@ -333,35 +158,35 @@ class TableClient {
333
158
  const hasSkip = finalArgs?.skip !== undefined && finalArgs?.skip !== null;
334
159
  let cols = "*";
335
160
  if (finalArgs?.select) {
336
- const modelRelations = an5Metadata_1.relationMap[this.modelName] || {};
161
+ const modelRelations = this.orm.metadata.relationMap[this.modelName] || {};
337
162
  const selectedCols = Object.keys(finalArgs.select)
338
163
  .filter(k => finalArgs.select[k] && !modelRelations[k])
339
- .map(k => `[${k}]`);
164
+ .map(k => (0, sql_utils_1.quoteIdentifier)(k));
340
165
  if (selectedCols.length > 0) {
341
166
  cols = selectedCols.join(", ");
342
167
  }
343
168
  }
344
169
  let sqlText = "SELECT";
345
170
  if (finalArgs?.take && !hasSkip) {
346
- sqlText += ` TOP (${finalArgs.take})`;
171
+ sqlText += ` TOP (${(0, sql_utils_1.toNonNegativeInt)(finalArgs.take, 1)})`;
347
172
  }
348
173
  sqlText += ` ${cols} FROM ${this.tableName} WITH (NOLOCK)`;
349
174
  const p = {};
350
- const whereSql = parseWhere(this.modelName, finalArgs?.where, p);
175
+ const whereSql = this.orm.parseWhere(this.modelName, finalArgs?.where, p);
351
176
  if (whereSql) {
352
177
  sqlText += ` WHERE ${whereSql}`;
353
178
  }
354
179
  if (finalArgs?.orderBy) {
355
- sqlText += buildOrderBy(finalArgs.orderBy);
180
+ sqlText += (0, sql_utils_1.buildOrderBy)(finalArgs.orderBy);
356
181
  }
357
182
  else if (hasSkip) {
358
183
  // OFFSET requires an ORDER BY clause in SQL Server
359
184
  sqlText += " ORDER BY (SELECT NULL)";
360
185
  }
361
186
  if (hasSkip) {
362
- sqlText += ` OFFSET ${finalArgs.skip} ROWS`;
187
+ sqlText += ` OFFSET ${(0, sql_utils_1.toNonNegativeInt)(finalArgs.skip)} ROWS`;
363
188
  if (finalArgs?.take) {
364
- sqlText += ` FETCH NEXT ${finalArgs.take} ROWS ONLY`;
189
+ sqlText += ` FETCH NEXT ${(0, sql_utils_1.toNonNegativeInt)(finalArgs.take, 1)} ROWS ONLY`;
365
190
  }
366
191
  }
367
192
  const rows = await this.executor(sqlText, p);
@@ -371,7 +196,7 @@ class TableClient {
371
196
  });
372
197
  }
373
198
  if (finalArgs?.include) {
374
- await resolveIncludes(this.modelName, rows, finalArgs.include, this.executor);
199
+ await resolveIncludes(this.modelName, rows, finalArgs.include, this.executor, this.orm.metadata);
375
200
  }
376
201
  return rows;
377
202
  });
@@ -392,7 +217,7 @@ class TableClient {
392
217
  const { args: finalArgs } = params;
393
218
  let sqlText = `SELECT COUNT(*) as count FROM ${this.tableName} WITH (NOLOCK)`;
394
219
  const p = {};
395
- const whereSql = parseWhere(this.modelName, finalArgs?.where, p);
220
+ const whereSql = this.orm.parseWhere(this.modelName, finalArgs?.where, p);
396
221
  if (whereSql) {
397
222
  sqlText += ` WHERE ${whereSql}`;
398
223
  }
@@ -401,7 +226,7 @@ class TableClient {
401
226
  });
402
227
  }
403
228
  async handleNestedWrites(data, parentId) {
404
- const modelRelations = an5Metadata_1.relationMap[this.modelName] || {};
229
+ const modelRelations = this.orm.metadata.relationMap[this.modelName] || {};
405
230
  for (const [key, value] of Object.entries(data)) {
406
231
  const relation = modelRelations[key];
407
232
  if (!relation || !value || typeof value !== "object" || (value instanceof Date))
@@ -447,20 +272,20 @@ class TableClient {
447
272
  const data = { ...finalArgs.data };
448
273
  // Extract nested writes
449
274
  const nestedData = {};
450
- const modelRelations = an5Metadata_1.relationMap[this.modelName] || {};
275
+ const modelRelations = this.orm.metadata.relationMap[this.modelName] || {};
451
276
  for (const key of Object.keys(data)) {
452
277
  if (modelRelations[key]) {
453
278
  nestedData[key] = data[key];
454
279
  delete data[key];
455
280
  }
456
281
  }
457
- if (!data.id && an5Metadata_1.modelFields[this.modelName]?.id?.ts === "string") {
282
+ if (!data.id && this.orm.metadata.modelFields[this.modelName]?.id?.ts === "string") {
458
283
  data.id = (0, crypto_1.randomUUID)();
459
284
  }
460
285
  const now = new Date();
461
- if (!data.createdAt && an5Metadata_1.modelFields[this.modelName]?.createdAt)
286
+ if (!data.createdAt && this.orm.metadata.modelFields[this.modelName]?.createdAt)
462
287
  data.createdAt = now;
463
- if (!data.updatedAt && an5Metadata_1.modelFields[this.modelName]?.updatedAt)
288
+ if (!data.updatedAt && this.orm.metadata.modelFields[this.modelName]?.updatedAt)
464
289
  data.updatedAt = now;
465
290
  // Handle one-relation connect where we hold the FK
466
291
  for (const [key, value] of Object.entries(nestedData)) {
@@ -472,10 +297,13 @@ class TableClient {
472
297
  }
473
298
  }
474
299
  const keys = Object.keys(data);
475
- const columns = keys.map(k => `[${k}]`).join(", ");
476
- const placeholders = keys.map(k => `@${k}`).join(", ");
300
+ const columns = keys.map(k => (0, sql_utils_1.quoteIdentifier)(k)).join(", ");
301
+ const safeKeys = keys.map(k => (0, sql_utils_1.sanitizeParamName)(k));
302
+ const placeholders = safeKeys.map(k => `@${k}`).join(", ");
303
+ const safeData = {};
304
+ keys.forEach((k, i) => { safeData[safeKeys[i]] = data[k]; });
477
305
  const sqlText = `INSERT INTO ${this.tableName} (${columns}) OUTPUT inserted.* VALUES (${placeholders})`;
478
- const rows = await this.executor(sqlText, data);
306
+ const rows = await this.executor(sqlText, safeData);
479
307
  const createdRow = rows[0];
480
308
  if (!createdRow) {
481
309
  throw new Error("Failed to insert record and retrieve output");
@@ -500,19 +328,19 @@ class TableClient {
500
328
  msg.includes('unique') ||
501
329
  errNumber === 2627 ||
502
330
  errNumber === 2601) {
503
- throw new typescript_1.An5.An5ClientKnownRequestError("Unique constraint failed", {
331
+ throw new errors_1.An5ClientKnownRequestError("Unique constraint failed", {
504
332
  code: "P2002",
505
333
  clientVersion: "mock",
506
334
  });
507
335
  }
508
336
  if (msg.includes('foreign key') || errNumber === 547) {
509
- throw new typescript_1.An5.An5ClientKnownRequestError("Foreign key constraint failed", {
337
+ throw new errors_1.An5ClientKnownRequestError("Foreign key constraint failed", {
510
338
  code: "P2003",
511
339
  clientVersion: "mock",
512
340
  });
513
341
  }
514
342
  if (msg.includes('not found') || errNumber === 404) {
515
- throw new typescript_1.An5.An5ClientKnownRequestError("Record not found", {
343
+ throw new errors_1.An5ClientKnownRequestError("Record not found", {
516
344
  code: "P2025",
517
345
  clientVersion: "mock",
518
346
  });
@@ -525,12 +353,12 @@ class TableClient {
525
353
  return this.orm._executeMiddleware({ model: this.modelName, action: 'update', args }, async (params) => {
526
354
  const { args: finalArgs } = params;
527
355
  const data = { ...finalArgs.data };
528
- if (an5Metadata_1.modelFields[this.modelName]?.updatedAt) {
356
+ if (this.orm.metadata.modelFields[this.modelName]?.updatedAt) {
529
357
  data.updatedAt = new Date();
530
358
  }
531
359
  // Extract nested writes
532
360
  const nestedData = {};
533
- const modelRelations = an5Metadata_1.relationMap[this.modelName] || {};
361
+ const modelRelations = this.orm.metadata.relationMap[this.modelName] || {};
534
362
  for (const key of Object.keys(data)) {
535
363
  if (modelRelations[key]) {
536
364
  nestedData[key] = data[key];
@@ -551,28 +379,30 @@ class TableClient {
551
379
  const p = {};
552
380
  for (const key of Object.keys(data)) {
553
381
  const val = data[key];
382
+ const col = (0, sql_utils_1.quoteIdentifier)(key);
383
+ const safeKey = (0, sql_utils_1.sanitizeParamName)(key);
554
384
  if (val && typeof val === "object" && !(val instanceof Date)) {
555
385
  if (val.increment !== undefined) {
556
- sets.push(`[${key}] = [${key}] + @${key}_inc`);
557
- p[`${key}_inc`] = val.increment;
386
+ sets.push(`${col} = ${col} + @${safeKey}_inc`);
387
+ p[`${safeKey}_inc`] = val.increment;
558
388
  continue;
559
389
  }
560
390
  else if (val.decrement !== undefined) {
561
- sets.push(`[${key}] = [${key}] - @${key}_dec`);
562
- p[`${key}_dec`] = val.decrement;
391
+ sets.push(`${col} = ${col} - @${safeKey}_dec`);
392
+ p[`${safeKey}_dec`] = val.decrement;
563
393
  continue;
564
394
  }
565
395
  else if (val.set !== undefined) {
566
- sets.push(`[${key}] = @${key}_set`);
567
- p[`${key}_set`] = val.set;
396
+ sets.push(`${col} = @${safeKey}_set`);
397
+ p[`${safeKey}_set`] = val.set;
568
398
  continue;
569
399
  }
570
400
  }
571
- sets.push(`[${key}] = @${key}`);
572
- p[key] = val;
401
+ sets.push(`${col} = @${safeKey}`);
402
+ p[safeKey] = val;
573
403
  }
574
404
  const whereParams = {};
575
- const whereSql = parseWhere(this.modelName, finalArgs.where, whereParams, "w_");
405
+ const whereSql = this.orm.parseWhere(this.modelName, finalArgs.where, whereParams, "w_");
576
406
  Object.assign(p, whereParams);
577
407
  const sqlText = `UPDATE ${this.tableName} SET ${sets.join(", ")} WHERE ${whereSql}`;
578
408
  await this.executor(sqlText, p);
@@ -594,7 +424,7 @@ class TableClient {
594
424
  return this.orm._executeMiddleware({ model: this.modelName, action: 'updateMany', args }, async (params) => {
595
425
  const { args: finalArgs } = params;
596
426
  const data = { ...finalArgs.data };
597
- if (an5Metadata_1.modelFields[this.modelName]?.updatedAt) {
427
+ if (this.orm.metadata.modelFields[this.modelName]?.updatedAt) {
598
428
  data.updatedAt = new Date();
599
429
  }
600
430
  delete data.id;
@@ -602,39 +432,36 @@ class TableClient {
602
432
  const p = {};
603
433
  for (const key of Object.keys(data)) {
604
434
  const val = data[key];
605
- if (an5Metadata_1.relationMap[this.modelName]?.[key])
435
+ if (this.orm.metadata.relationMap[this.modelName]?.[key])
606
436
  continue;
437
+ const col = (0, sql_utils_1.quoteIdentifier)(key);
438
+ const safeKey = (0, sql_utils_1.sanitizeParamName)(key);
607
439
  if (val && typeof val === "object" && !(val instanceof Date)) {
608
440
  if (val.increment !== undefined) {
609
- sets.push(`[${key}] = [${key}] + @${key}_inc`);
610
- p[`${key}_inc`] = val.increment;
441
+ sets.push(`${col} = ${col} + @${safeKey}_inc`);
442
+ p[`${safeKey}_inc`] = val.increment;
611
443
  continue;
612
444
  }
613
445
  else if (val.decrement !== undefined) {
614
- sets.push(`[${key}] = [${key}] - @${key}_dec`);
615
- p[`${key}_dec`] = val.decrement;
446
+ sets.push(`${col} = ${col} - @${safeKey}_dec`);
447
+ p[`${safeKey}_dec`] = val.decrement;
616
448
  continue;
617
449
  }
618
450
  else if (val.set !== undefined) {
619
- sets.push(`[${key}] = @${key}_set`);
620
- p[`${key}_set`] = val.set;
451
+ sets.push(`${col} = @${safeKey}_set`);
452
+ p[`${safeKey}_set`] = val.set;
621
453
  continue;
622
454
  }
623
455
  }
624
- sets.push(`[${key}] = @${key}`);
625
- p[key] = val;
456
+ sets.push(`${col} = @${safeKey}`);
457
+ p[safeKey] = val;
626
458
  }
627
459
  const whereParams = {};
628
- const whereSql = parseWhere(this.modelName, finalArgs.where, whereParams, "w_");
460
+ const whereSql = this.orm.parseWhere(this.modelName, finalArgs.where, whereParams, "w_");
629
461
  Object.assign(p, whereParams);
630
462
  const sqlText = `UPDATE ${this.tableName} SET ${sets.join(", ")} ${whereSql ? `WHERE ${whereSql}` : ""}`;
631
- const pool = await (await getAdapter()).getPool();
632
- const request = new mssql_1.default.Request(pool);
633
- for (const [key, value] of Object.entries(p)) {
634
- request.input(key, value);
635
- }
636
- const result = await request.query(sqlText);
637
- return { count: result.rowsAffected?.[0] || 0 };
463
+ const count = await (await getAdapter())._executeRaw(sqlText, p);
464
+ return { count };
638
465
  });
639
466
  }
640
467
  async delete(args) {
@@ -644,7 +471,7 @@ class TableClient {
644
471
  if (!existing)
645
472
  throw new Error("Record not found to delete");
646
473
  const p = {};
647
- const whereSql = parseWhere(this.modelName, finalArgs.where, p);
474
+ const whereSql = this.orm.parseWhere(this.modelName, finalArgs.where, p);
648
475
  const sqlText = `DELETE FROM ${this.tableName} WHERE ${whereSql}`;
649
476
  await this.executor(sqlText, p);
650
477
  return existing;
@@ -654,39 +481,37 @@ class TableClient {
654
481
  return this.orm._executeMiddleware({ model: this.modelName, action: 'deleteMany', args }, async (params) => {
655
482
  const { args: finalArgs } = params;
656
483
  const p = {};
657
- const whereSql = parseWhere(this.modelName, finalArgs?.where, p);
484
+ const whereSql = this.orm.parseWhere(this.modelName, finalArgs?.where, p);
658
485
  const sqlText = `DELETE FROM ${this.tableName} ${whereSql ? `WHERE ${whereSql}` : ""}`;
659
- const pool = await (await getAdapter()).getPool();
660
- const request = new mssql_1.default.Request(pool);
661
- for (const [key, value] of Object.entries(p)) {
662
- request.input(key, value);
663
- }
664
- const result = await request.query(sqlText);
665
- return { count: result.rowsAffected?.[0] || 0 };
486
+ const count = await (await getAdapter())._executeRaw(sqlText, p);
487
+ return { count };
666
488
  });
667
489
  }
668
490
  async vectorSearch(args) {
669
491
  return this.orm._executeMiddleware({ model: this.modelName, action: 'vectorSearch', args }, async (params) => {
670
492
  const { args: finalArgs } = params;
671
493
  const field = finalArgs.vectorField || "embedding";
672
- const metric = finalArgs.distanceMetric || "cosine";
673
- const elementType = finalArgs.vectorElementType || "float32";
674
- const take = finalArgs.take || 10;
675
- const dim = finalArgs.vector.length;
494
+ const METRICS = ["cosine", "euclidean", "dot"];
495
+ const ELEMENT_TYPES = ["float32", "float16", "uint8"];
496
+ const metric = METRICS.includes(finalArgs.distanceMetric) ? finalArgs.distanceMetric : "cosine";
497
+ const elementType = ELEMENT_TYPES.includes(finalArgs.vectorElementType) ? finalArgs.vectorElementType : "float32";
498
+ const take = (0, sql_utils_1.toNonNegativeInt)(finalArgs.take, 10);
499
+ const dim = Number(finalArgs.vector?.length) || 0;
676
500
  const vectorJson = JSON.stringify(finalArgs.vector);
501
+ const col = (0, sql_utils_1.quoteIdentifier)(field);
677
502
  try {
678
503
  if (dim > 1998) {
679
504
  throw new Error("Vector dimension exceeds SQL Server limit of 1998");
680
505
  }
681
506
  let sqlText = `SELECT TOP (${take}) *, `;
682
- sqlText += `VECTOR_DISTANCE('${metric}', CAST([${field}] AS VECTOR(${dim}, ${elementType})), CAST(@query_vector AS VECTOR(${dim}, ${elementType}))) AS distance `;
507
+ sqlText += `VECTOR_DISTANCE('${metric}', CAST(${col} AS VECTOR(${dim}, ${elementType})), CAST(@query_vector AS VECTOR(${dim}, ${elementType}))) AS distance `;
683
508
  sqlText += `FROM ${this.tableName} WITH (NOLOCK) `;
684
509
  const p = {
685
510
  query_vector: vectorJson
686
511
  };
687
512
  const whereClauses = [];
688
- whereClauses.push(`[${field}] IS NOT NULL`);
689
- const whereSql = parseWhere(this.modelName, finalArgs.where, p, "v_");
513
+ whereClauses.push(`${col} IS NOT NULL`);
514
+ const whereSql = this.orm.parseWhere(this.modelName, finalArgs.where, p, "v_");
690
515
  if (whereSql) {
691
516
  whereClauses.push(whereSql);
692
517
  }
@@ -694,7 +519,7 @@ class TableClient {
694
519
  sqlText += `ORDER BY distance ASC`;
695
520
  const rows = await this.executor(sqlText, p);
696
521
  if (finalArgs.include) {
697
- await resolveIncludes(this.modelName, rows, finalArgs.include, this.executor);
522
+ await resolveIncludes(this.modelName, rows, finalArgs.include, this.executor, this.orm.metadata);
698
523
  }
699
524
  return rows;
700
525
  }
@@ -716,8 +541,8 @@ class TableClient {
716
541
  let fallbackSql = `SELECT * FROM ${this.tableName} WITH (NOLOCK) `;
717
542
  const fallbackParams = {};
718
543
  const fallbackWhereClauses = [];
719
- fallbackWhereClauses.push(`[${field}] IS NOT NULL`);
720
- const fallbackWhereSql = parseWhere(this.modelName, finalArgs.where, fallbackParams, "vf_");
544
+ fallbackWhereClauses.push(`${col} IS NOT NULL`);
545
+ const fallbackWhereSql = this.orm.parseWhere(this.modelName, finalArgs.where, fallbackParams, "vf_");
721
546
  if (fallbackWhereSql) {
722
547
  fallbackWhereClauses.push(fallbackWhereSql);
723
548
  }
@@ -754,7 +579,7 @@ class TableClient {
754
579
  .sort((a, b) => a.distance - b.distance)
755
580
  .slice(0, take);
756
581
  if (finalArgs.include && results.length > 0) {
757
- await resolveIncludes(this.modelName, results, finalArgs.include, this.executor);
582
+ await resolveIncludes(this.modelName, results, finalArgs.include, this.executor, this.orm.metadata);
758
583
  }
759
584
  return results;
760
585
  }
@@ -766,42 +591,10 @@ class TableClient {
766
591
  if (!finalArgs.data || finalArgs.data.length === 0)
767
592
  return { count: 0 };
768
593
  try {
769
- const pool = await (await getAdapter()).getPool();
770
- const table = new mssql_1.default.Table(this.tableName);
771
- const fields = an5Metadata_1.modelFields[this.modelName] || {};
594
+ const fields = this.orm.metadata.modelFields[this.modelName] || {};
772
595
  const fieldNames = Object.keys(fields);
773
- for (const col of fieldNames) {
774
- const fieldDef = fields[col];
775
- const rawType = fieldDef?.ts || 'string';
776
- const sqlType = fieldDef?.sql || 'NVARCHAR(MAX)';
777
- const isNullable = rawType.endsWith('?');
778
- const tsType = isNullable ? rawType.slice(0, -1) : rawType;
779
- // Map SQL Server type to mssql package type
780
- const sqlBase = sqlType.toUpperCase().split('(')[0];
781
- if (sqlBase === 'INT' || sqlBase === 'SMALLINT' || sqlBase === 'TINYINT') {
782
- table.columns.add(col, mssql_1.default.Int, { nullable: isNullable });
783
- }
784
- else if (sqlBase === 'BIGINT') {
785
- table.columns.add(col, mssql_1.default.BigInt, { nullable: isNullable });
786
- }
787
- else if (sqlBase === 'FLOAT' || sqlBase === 'REAL' || sqlBase === 'DECIMAL' || sqlBase === 'NUMERIC' || sqlBase === 'MONEY' || sqlBase === 'SMALLMONEY') {
788
- table.columns.add(col, mssql_1.default.Float, { nullable: isNullable });
789
- }
790
- else if (sqlBase === 'BIT') {
791
- table.columns.add(col, mssql_1.default.Bit, { nullable: isNullable });
792
- }
793
- else if (sqlBase === 'DATETIME' || sqlBase === 'DATETIME2' || sqlBase === 'SMALLDATETIME' || sqlBase === 'DATE' || sqlBase === 'TIME') {
794
- table.columns.add(col, mssql_1.default.DateTime, { nullable: isNullable });
795
- }
796
- else if (sqlBase === 'UNIQUEIDENTIFIER') {
797
- table.columns.add(col, mssql_1.default.UniqueIdentifier, { nullable: isNullable });
798
- }
799
- else {
800
- table.columns.add(col, mssql_1.default.NVarChar(mssql_1.default.MAX), { nullable: isNullable });
801
- }
802
- }
803
596
  const now = new Date();
804
- for (const item of finalArgs.data) {
597
+ const rows = finalArgs.data.map((item) => {
805
598
  const rowData = { ...item };
806
599
  if (!rowData.id && fields.id?.ts === "string")
807
600
  rowData.id = (0, crypto_1.randomUUID)();
@@ -809,17 +602,24 @@ class TableClient {
809
602
  rowData.createdAt = now;
810
603
  if (!rowData.updatedAt && fields.updatedAt)
811
604
  rowData.updatedAt = now;
812
- const rowValues = fieldNames.map(col => {
813
- const v = rowData[col];
814
- if (v === undefined)
815
- return null;
816
- return v;
605
+ return rowData;
606
+ });
607
+ const cols = fieldNames.filter(col => rows.some((r) => r[col] !== undefined));
608
+ if (cols.length === 0)
609
+ throw new Error("No insertable columns");
610
+ const params = {};
611
+ const rowPlaceholders = [];
612
+ rows.forEach((row, rowIdx) => {
613
+ const vals = cols.map(col => {
614
+ const p = (0, sql_utils_1.sanitizeParamName)(`r${rowIdx}_${col}`);
615
+ params[p] = row[col] ?? null;
616
+ return `@${p}`;
817
617
  });
818
- table.rows.add(...rowValues);
819
- }
820
- const request = new mssql_1.default.Request(pool);
821
- await request.bulk(table);
822
- return { count: finalArgs.data.length };
618
+ rowPlaceholders.push(`(${vals.join(", ")})`);
619
+ });
620
+ const sqlText = `INSERT INTO ${this.tableName} (${cols.map(sql_utils_1.quoteIdentifier).join(", ")}) VALUES ${rowPlaceholders.join(", ")}`;
621
+ await (await getAdapter())._executeRaw(sqlText, params);
622
+ return { count: rows.length };
823
623
  }
824
624
  catch (err) {
825
625
  logger_1.logger.warn(`Bulk insert failed, falling back to sequential inserts: ${err.message}`);
@@ -847,25 +647,25 @@ class TableClient {
847
647
  if (finalArgs._sum) {
848
648
  resultObj._sum = {};
849
649
  for (const key of Object.keys(finalArgs._sum)) {
850
- selects.push(`SUM(${key}) as sum_${key}`);
650
+ selects.push(aggSelect("SUM", key));
851
651
  }
852
652
  }
853
653
  if (finalArgs._avg) {
854
654
  resultObj._avg = {};
855
655
  for (const key of Object.keys(finalArgs._avg)) {
856
- selects.push(`AVG(${key}) as avg_${key}`);
656
+ selects.push(aggSelect("AVG", key));
857
657
  }
858
658
  }
859
659
  if (finalArgs._min) {
860
660
  resultObj._min = {};
861
661
  for (const key of Object.keys(finalArgs._min)) {
862
- selects.push(`MIN(${key}) as min_${key}`);
662
+ selects.push(aggSelect("MIN", key));
863
663
  }
864
664
  }
865
665
  if (finalArgs._max) {
866
666
  resultObj._max = {};
867
667
  for (const key of Object.keys(finalArgs._max)) {
868
- selects.push(`MAX(${key}) as max_${key}`);
668
+ selects.push(aggSelect("MAX", key));
869
669
  }
870
670
  }
871
671
  if (finalArgs._count) {
@@ -875,7 +675,7 @@ class TableClient {
875
675
  }
876
676
  else {
877
677
  for (const key of Object.keys(finalArgs._count)) {
878
- selects.push(`COUNT(${key}) as count_${key}`);
678
+ selects.push(aggSelect("COUNT", key));
879
679
  }
880
680
  }
881
681
  }
@@ -884,7 +684,7 @@ class TableClient {
884
684
  }
885
685
  let sqlText = `SELECT ${selects.join(", ")} FROM ${this.tableName} WITH (NOLOCK)`;
886
686
  const p = {};
887
- const whereSql = parseWhere(this.modelName, finalArgs?.where, p);
687
+ const whereSql = this.orm.parseWhere(this.modelName, finalArgs?.where, p);
888
688
  if (whereSql) {
889
689
  sqlText += ` WHERE ${whereSql}`;
890
690
  }
@@ -892,22 +692,22 @@ class TableClient {
892
692
  const row = rows[0] || {};
893
693
  if (finalArgs._sum) {
894
694
  for (const key of Object.keys(finalArgs._sum)) {
895
- resultObj._sum[key] = row[`sum_${key}`] !== undefined ? row[`sum_${key}`] : null;
695
+ resultObj._sum[key] = row[aggAlias("sum", key)] !== undefined ? row[aggAlias("sum", key)] : null;
896
696
  }
897
697
  }
898
698
  if (finalArgs._avg) {
899
699
  for (const key of Object.keys(finalArgs._avg)) {
900
- resultObj._avg[key] = row[`avg_${key}`] !== undefined ? row[`avg_${key}`] : null;
700
+ resultObj._avg[key] = row[aggAlias("avg", key)] !== undefined ? row[aggAlias("avg", key)] : null;
901
701
  }
902
702
  }
903
703
  if (finalArgs._min) {
904
704
  for (const key of Object.keys(finalArgs._min)) {
905
- resultObj._min[key] = row[`min_${key}`] !== undefined ? row[`min_${key}`] : null;
705
+ resultObj._min[key] = row[aggAlias("min", key)] !== undefined ? row[aggAlias("min", key)] : null;
906
706
  }
907
707
  }
908
708
  if (finalArgs._max) {
909
709
  for (const key of Object.keys(finalArgs._max)) {
910
- resultObj._max[key] = row[`max_${key}`] !== undefined ? row[`max_${key}`] : null;
710
+ resultObj._max[key] = row[aggAlias("max", key)] !== undefined ? row[aggAlias("max", key)] : null;
911
711
  }
912
712
  }
913
713
  if (finalArgs._count) {
@@ -916,7 +716,7 @@ class TableClient {
916
716
  }
917
717
  else {
918
718
  for (const key of Object.keys(finalArgs._count)) {
919
- resultObj._count[key] = row[`count_${key}`] || 0;
719
+ resultObj._count[key] = row[aggAlias("count", key)] || 0;
920
720
  }
921
721
  }
922
722
  }
@@ -930,44 +730,44 @@ class TableClient {
930
730
  if (byFields.length === 0) {
931
731
  throw new Error("groupBy requires 'by' fields");
932
732
  }
933
- const selects = [...byFields];
733
+ const selects = byFields.map((f) => (0, sql_utils_1.quoteIdentifier)(f));
934
734
  if (finalArgs._count) {
935
735
  if (finalArgs._count === true || finalArgs._count._all) {
936
736
  selects.push(`COUNT(*) as count_all`);
937
737
  }
938
738
  else {
939
739
  for (const key of Object.keys(finalArgs._count)) {
940
- selects.push(`COUNT(${key}) as count_${key}`);
740
+ selects.push(aggSelect("COUNT", key));
941
741
  }
942
742
  }
943
743
  }
944
744
  if (finalArgs._sum) {
945
745
  for (const key of Object.keys(finalArgs._sum)) {
946
- selects.push(`SUM(${key}) as sum_${key}`);
746
+ selects.push(aggSelect("SUM", key));
947
747
  }
948
748
  }
949
749
  if (finalArgs._avg) {
950
750
  for (const key of Object.keys(finalArgs._avg)) {
951
- selects.push(`AVG(${key}) as avg_${key}`);
751
+ selects.push(aggSelect("AVG", key));
952
752
  }
953
753
  }
954
754
  if (finalArgs._min) {
955
755
  for (const key of Object.keys(finalArgs._min)) {
956
- selects.push(`MIN(${key}) as min_${key}`);
756
+ selects.push(aggSelect("MIN", key));
957
757
  }
958
758
  }
959
759
  if (finalArgs._max) {
960
760
  for (const key of Object.keys(finalArgs._max)) {
961
- selects.push(`MAX(${key}) as max_${key}`);
761
+ selects.push(aggSelect("MAX", key));
962
762
  }
963
763
  }
964
764
  let sqlText = `SELECT ${selects.join(", ")} FROM ${this.tableName} WITH (NOLOCK)`;
965
765
  const p = {};
966
- const whereSql = parseWhere(this.modelName, finalArgs?.where, p);
766
+ const whereSql = this.orm.parseWhere(this.modelName, finalArgs?.where, p);
967
767
  if (whereSql) {
968
768
  sqlText += ` WHERE ${whereSql}`;
969
769
  }
970
- sqlText += ` GROUP BY ${byFields.join(", ")}`;
770
+ sqlText += ` GROUP BY ${byFields.map((f) => (0, sql_utils_1.quoteIdentifier)(f)).join(", ")}`;
971
771
  const rows = await this.executor(sqlText, p);
972
772
  return rows.map((row) => {
973
773
  const item = {};
@@ -981,32 +781,32 @@ class TableClient {
981
781
  }
982
782
  else {
983
783
  for (const key of Object.keys(finalArgs._count)) {
984
- item._count[key] = row[`count_${key}`] || 0;
784
+ item._count[key] = row[aggAlias("count", key)] || 0;
985
785
  }
986
786
  }
987
787
  }
988
788
  if (finalArgs._sum) {
989
789
  item._sum = {};
990
790
  for (const key of Object.keys(finalArgs._sum)) {
991
- item._sum[key] = row[`sum_${key}`] !== undefined ? row[`sum_${key}`] : null;
791
+ item._sum[key] = row[aggAlias("sum", key)] !== undefined ? row[aggAlias("sum", key)] : null;
992
792
  }
993
793
  }
994
794
  if (finalArgs._avg) {
995
795
  item._avg = {};
996
796
  for (const key of Object.keys(finalArgs._avg)) {
997
- item._avg[key] = row[`avg_${key}`] !== undefined ? row[`avg_${key}`] : null;
797
+ item._avg[key] = row[aggAlias("avg", key)] !== undefined ? row[aggAlias("avg", key)] : null;
998
798
  }
999
799
  }
1000
800
  if (finalArgs._min) {
1001
801
  item._min = {};
1002
802
  for (const key of Object.keys(finalArgs._min)) {
1003
- item._min[key] = row[`min_${key}`] !== undefined ? row[`min_${key}`] : null;
803
+ item._min[key] = row[aggAlias("min", key)] !== undefined ? row[aggAlias("min", key)] : null;
1004
804
  }
1005
805
  }
1006
806
  if (finalArgs._max) {
1007
807
  item._max = {};
1008
808
  for (const key of Object.keys(finalArgs._max)) {
1009
- item._max[key] = row[`max_${key}`] !== undefined ? row[`max_${key}`] : null;
809
+ item._max[key] = row[aggAlias("max", key)] !== undefined ? row[aggAlias("max", key)] : null;
1010
810
  }
1011
811
  }
1012
812
  return item;
@@ -1021,39 +821,41 @@ class TableClient {
1021
821
  const cleanCreate = { ...createData };
1022
822
  const cleanUpdate = { ...updateData };
1023
823
  for (const key of Object.keys(cleanCreate)) {
1024
- if (an5Metadata_1.relationMap[this.modelName]?.[key])
824
+ if (this.orm.metadata.relationMap[this.modelName]?.[key])
1025
825
  delete cleanCreate[key];
1026
826
  }
1027
827
  for (const key of Object.keys(cleanUpdate)) {
1028
- if (an5Metadata_1.relationMap[this.modelName]?.[key])
828
+ if (this.orm.metadata.relationMap[this.modelName]?.[key])
1029
829
  delete cleanUpdate[key];
1030
830
  }
1031
- if (!cleanCreate.id && an5Metadata_1.modelFields[this.modelName]?.id?.ts === "string") {
831
+ if (!cleanCreate.id && this.orm.metadata.modelFields[this.modelName]?.id?.ts === "string") {
1032
832
  cleanCreate.id = (0, crypto_1.randomUUID)();
1033
833
  }
1034
834
  const now = new Date();
1035
- if (!cleanCreate.createdAt && an5Metadata_1.modelFields[this.modelName]?.createdAt)
835
+ if (!cleanCreate.createdAt && this.orm.metadata.modelFields[this.modelName]?.createdAt)
1036
836
  cleanCreate.createdAt = now;
1037
- if (!cleanCreate.updatedAt && an5Metadata_1.modelFields[this.modelName]?.updatedAt)
837
+ if (!cleanCreate.updatedAt && this.orm.metadata.modelFields[this.modelName]?.updatedAt)
1038
838
  cleanCreate.updatedAt = now;
1039
- if (!cleanUpdate.updatedAt && an5Metadata_1.modelFields[this.modelName]?.updatedAt)
839
+ if (!cleanUpdate.updatedAt && this.orm.metadata.modelFields[this.modelName]?.updatedAt)
1040
840
  cleanUpdate.updatedAt = now;
1041
841
  const p = {};
1042
- const whereSql = parseWhere(this.modelName, where, p, "upw_");
842
+ const whereSql = this.orm.parseWhere(this.modelName, where, p, "upw_");
1043
843
  const allKeys = Array.from(new Set([...Object.keys(cleanCreate), ...Object.keys(cleanUpdate)]));
844
+ const cParam = (k) => `c_${(0, sql_utils_1.sanitizeParamName)(k)}`;
845
+ const uParam = (k) => `u_${(0, sql_utils_1.sanitizeParamName)(k)}`;
1044
846
  for (const k of allKeys) {
1045
847
  if (cleanCreate[k] !== undefined)
1046
- p[`c_${k}`] = cleanCreate[k];
848
+ p[cParam(k)] = cleanCreate[k];
1047
849
  if (cleanUpdate[k] !== undefined)
1048
- p[`u_${k}`] = cleanUpdate[k];
850
+ p[uParam(k)] = cleanUpdate[k];
1049
851
  }
1050
852
  const sourceSelect = allKeys.map(k => {
1051
- const val = cleanCreate[k] !== undefined ? `@c_${k}` : (cleanUpdate[k] !== undefined ? `@u_${k}` : "NULL");
1052
- return `${val} as [${k}]`;
853
+ const val = cleanCreate[k] !== undefined ? `@${cParam(k)}` : (cleanUpdate[k] !== undefined ? `@${uParam(k)}` : "NULL");
854
+ return `${val} as ${(0, sql_utils_1.quoteIdentifier)(k)}`;
1053
855
  }).join(", ");
1054
- const updateSets = Object.keys(cleanUpdate).map(k => `target.[${k}] = source.[${k}]`).join(", ");
1055
- const insertCols = Object.keys(cleanCreate).map(k => `[${k}]`).join(", ");
1056
- const insertVals = Object.keys(cleanCreate).map(k => `source.[${k}]`).join(", ");
856
+ const updateSets = Object.keys(cleanUpdate).map(k => `target.${(0, sql_utils_1.quoteIdentifier)(k)} = source.${(0, sql_utils_1.quoteIdentifier)(k)}`).join(", ");
857
+ const insertCols = Object.keys(cleanCreate).map(k => (0, sql_utils_1.quoteIdentifier)(k)).join(", ");
858
+ const insertVals = Object.keys(cleanCreate).map(k => `source.${(0, sql_utils_1.quoteIdentifier)(k)}`).join(", ");
1057
859
  // Note: This MERGE assumes the 'where' translates to a simple ON clause.
1058
860
  // For complex 'where', parseWhere might return something not easily usable in ON.
1059
861
  // We'll try to extract simple equality for ON if possible, or fallback to sequential.
@@ -1061,25 +863,25 @@ class TableClient {
1061
863
  if (typeof where[k] === 'object' && where[k] !== null && !(where[k] instanceof Date)) {
1062
864
  // Flatten unique object if needed
1063
865
  const inner = where[k];
1064
- return Object.keys(inner).map(ik => `target.[${ik}] = @upw_${k}_${ik}`).join(" AND ");
866
+ return Object.keys(inner).map(ik => `target.${(0, sql_utils_1.quoteIdentifier)(ik)} = @${(0, sql_utils_1.sanitizeParamName)(`upw_${k}_${ik}`)}`).join(" AND ");
1065
867
  }
1066
- return `target.[${k}] = @upw_${k}`;
868
+ return `target.${(0, sql_utils_1.quoteIdentifier)(k)} = @${(0, sql_utils_1.sanitizeParamName)(`upw_${k}`)}`;
1067
869
  }).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.*;
870
+ const sqlText = `
871
+ MERGE INTO ${this.tableName} WITH (HOLDLOCK) AS target
872
+ USING (SELECT ${sourceSelect}) AS source
873
+ ON (${onClause})
874
+ WHEN MATCHED THEN
875
+ UPDATE SET ${updateSets}
876
+ WHEN NOT MATCHED THEN
877
+ INSERT (${insertCols}) VALUES (${insertVals})
878
+ OUTPUT inserted.*;
1077
879
  `;
1078
880
  try {
1079
881
  const rows = await this.executor(sqlText, p);
1080
882
  const result = rows[0];
1081
883
  if (include && result) {
1082
- await resolveIncludes(this.modelName, [result], include, this.executor);
884
+ await resolveIncludes(this.modelName, [result], include, this.executor, this.orm.metadata);
1083
885
  }
1084
886
  return result;
1085
887
  }
@@ -1096,12 +898,12 @@ class TableClient {
1096
898
  });
1097
899
  }
1098
900
  }
1099
- function addNoLockToQuery(sql) {
901
+ function addNoLockToQuery(sql, metadata) {
1100
902
  // If it's not a SELECT query, don't modify it
1101
903
  if (!/^\s*SELECT/i.test(sql)) {
1102
904
  return sql;
1103
905
  }
1104
- const tableNames = Object.values(an5Metadata_1.modelToTable);
906
+ const tableNames = Object.values(metadata.modelToTable);
1105
907
  let modifiedSql = sql;
1106
908
  for (const table of tableNames) {
1107
909
  const escapedTable = table.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
@@ -1130,11 +932,32 @@ function addNoLockToQuery(sql) {
1130
932
  }
1131
933
  return modifiedSql;
1132
934
  }
935
+ // Lazy-load generated schema metadata from the ORM's own generated copy so
936
+ // `new An5ORM()` resolves schema models out of the box without the core ever
937
+ // importing from the generated client package (client is generated FROM the ORM).
938
+ let autoMetadata = null;
939
+ function loadAutoMetadata() {
940
+ if (autoMetadata)
941
+ return autoMetadata;
942
+ try {
943
+ const m = require("./an5Metadata");
944
+ autoMetadata = {
945
+ modelToTable: m.modelToTable || {},
946
+ relationMap: m.relationMap || {},
947
+ modelFields: m.modelFields || {},
948
+ };
949
+ }
950
+ catch {
951
+ autoMetadata = metadata_1.DEFAULT_METADATA;
952
+ }
953
+ return autoMetadata;
954
+ }
1133
955
  // Proxied AN5 ORM client class
1134
956
  class An5ORM {
1135
- constructor(customExecutor) {
957
+ constructor(customExecutor, metadata) {
1136
958
  this.customExecutor = customExecutor;
1137
959
  this.middlewares = [];
960
+ this.metadata = metadata ?? loadAutoMetadata();
1138
961
  // Add default logging middleware
1139
962
  this.$use(async (params, next) => {
1140
963
  const start = Date.now();
@@ -1180,7 +1003,7 @@ class An5ORM {
1180
1003
  }
1181
1004
  if (!(prop in target) && typeof prop === "string" && !prop.startsWith("_")) {
1182
1005
  // Resolve modelName in camelCase and map to table name
1183
- const tableName = an5Metadata_1.modelToTable[prop];
1006
+ const tableName = target.metadata.modelToTable[prop];
1184
1007
  if (tableName) {
1185
1008
  target[prop] = new TableClient(prop, tableName, target.customExecutor || execQuery, target // Pass ORM instance for middleware access
1186
1009
  );
@@ -1193,6 +1016,12 @@ class An5ORM {
1193
1016
  $use(middleware) {
1194
1017
  this.middlewares.push(middleware);
1195
1018
  }
1019
+ parseWhere(modelName, where, params, prefix = "") {
1020
+ return (0, sql_utils_1.parseWhere)(modelName, where, params, prefix, {
1021
+ relationMap: this.metadata.relationMap,
1022
+ modelToTable: this.metadata.modelToTable,
1023
+ });
1024
+ }
1196
1025
  async _executeMiddleware(params, finalAction) {
1197
1026
  let index = -1;
1198
1027
  const dispatch = async (i, currentParams) => {
@@ -1243,7 +1072,7 @@ class An5ORM {
1243
1072
  else {
1244
1073
  throw new Error("Invalid query format for $queryRaw");
1245
1074
  }
1246
- queryText = addNoLockToQuery(queryText);
1075
+ queryText = addNoLockToQuery(queryText, this.metadata);
1247
1076
  return executor(queryText, params);
1248
1077
  }
1249
1078
  async $queryRawUnsafe(queryText, ...values) {
@@ -1255,7 +1084,7 @@ class An5ORM {
1255
1084
  params[paramName] = val;
1256
1085
  });
1257
1086
  }
1258
- const modifiedQueryText = addNoLockToQuery(queryText);
1087
+ const modifiedQueryText = addNoLockToQuery(queryText, this.metadata);
1259
1088
  const result = await executor(modifiedQueryText, params);
1260
1089
  return result;
1261
1090
  }
@@ -1286,7 +1115,7 @@ class An5ORM {
1286
1115
  else {
1287
1116
  throw new Error("Invalid query format for $executeRaw");
1288
1117
  }
1289
- queryText = addNoLockToQuery(queryText);
1118
+ queryText = addNoLockToQuery(queryText, this.metadata);
1290
1119
  const result = (await executor(queryText, params));
1291
1120
  return result.rowsAffected?.[0] || 0;
1292
1121
  }
@@ -1299,7 +1128,7 @@ class An5ORM {
1299
1128
  params[paramName] = val;
1300
1129
  });
1301
1130
  }
1302
- const modifiedQueryText = addNoLockToQuery(queryText);
1131
+ const modifiedQueryText = addNoLockToQuery(queryText, this.metadata);
1303
1132
  const result = (await executor(modifiedQueryText, params));
1304
1133
  return result.rowsAffected?.[0] || 0;
1305
1134
  }
@@ -1307,42 +1136,18 @@ class An5ORM {
1307
1136
  if (Array.isArray(fn)) {
1308
1137
  return Promise.all(fn);
1309
1138
  }
1310
- const pool = await getDbPool();
1311
- const transaction = new mssql_1.default.Transaction(pool);
1312
- await transaction.begin();
1313
- logger_1.logger.info("Database Transaction Initiated");
1314
- const txExecutor = async (queryText, params) => {
1315
- const request = new mssql_1.default.Request(transaction);
1316
- if (params) {
1317
- for (const [key, value] of Object.entries(params)) {
1318
- request.input(key, value);
1319
- }
1320
- }
1321
- const result = await request.query(queryText);
1322
- const recordset = (result.recordset || []);
1323
- recordset.rowsAffected = result.rowsAffected;
1324
- return recordset;
1325
- };
1326
- const txClient = new An5ORM(txExecutor);
1327
- try {
1328
- const result = await fn(txClient);
1329
- await transaction.commit();
1330
- logger_1.logger.info("Database Transaction Committed Successfully");
1331
- return result;
1332
- }
1333
- catch (err) {
1334
- try {
1335
- await transaction.rollback();
1336
- logger_1.logger.warn("Database Transaction Rolled Back");
1337
- }
1338
- catch (rollbackErr) {
1339
- logger_1.logger.error("Failed to rollback database transaction", rollbackErr);
1340
- }
1341
- throw err;
1342
- }
1139
+ const a = await getAdapter();
1140
+ return a.$transaction(async (tx) => {
1141
+ const txExecutor = async (queryText, params) => {
1142
+ const rows = await tx.exec(queryText, params);
1143
+ return rows;
1144
+ };
1145
+ const txClient = new An5ORM(txExecutor, this.metadata);
1146
+ return fn(txClient);
1147
+ }, options);
1343
1148
  }
1344
1149
  }
1345
1150
  exports.An5ORM = An5ORM;
1346
1151
  exports.an5Orm = new An5ORM();
1347
1152
  exports.default = exports.an5Orm;
1348
- //# sourceMappingURL=an5Orm.js.map
1153
+ //# sourceMappingURL=an5Orm.js.map