@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
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { RelationDef } from "./metadata";
|
|
2
|
+
/**
|
|
3
|
+
* SQL Server identifier quoting.
|
|
4
|
+
*
|
|
5
|
+
* Wraps a bare identifier in square brackets and escapes any embedded
|
|
6
|
+
* closing bracket (`]` → `]]`), which is the only character that can break
|
|
7
|
+
* out of a bracketed identifier in T-SQL. This blocks SQL injection through
|
|
8
|
+
* user-supplied column / alias names.
|
|
9
|
+
*/
|
|
10
|
+
export declare function quoteIdentifier(name: string): string;
|
|
11
|
+
/** Quotes one-part or multipart SQL Server table identifiers safely. */
|
|
12
|
+
export declare function quoteTableIdentifier(name: string): string;
|
|
13
|
+
/** Sanitizes a parameter name to a safe T-SQL identifier fragment. */
|
|
14
|
+
export declare function sanitizeParamName(name: string): string;
|
|
15
|
+
/** Validates a direction token, defaulting to ASC. */
|
|
16
|
+
export declare function normalizeSortDirection(dir: unknown): "ASC" | "DESC";
|
|
17
|
+
/** Coerces a value to a non-negative integer, or returns the fallback. */
|
|
18
|
+
export declare function toNonNegativeInt(value: unknown, fallback?: number): number;
|
|
19
|
+
export interface QueryContext {
|
|
20
|
+
relationMap?: Record<string, Record<string, RelationDef>>;
|
|
21
|
+
modelToTable?: Record<string, string>;
|
|
22
|
+
}
|
|
23
|
+
export declare function buildOrderBy(orderBy: any): string;
|
|
24
|
+
/**
|
|
25
|
+
* Recursively builds a WHERE clause from a structured filter object.
|
|
26
|
+
*
|
|
27
|
+
* Values are always bound as parameters (`@name`); only identifiers are
|
|
28
|
+
* interpolated directly, and they are run through {@link quoteIdentifier}.
|
|
29
|
+
*/
|
|
30
|
+
export declare function parseWhere(modelName: string, where: any, params: Record<string, any>, prefix?: string, ctx?: QueryContext): string;
|
|
31
|
+
//# sourceMappingURL=sql-utils.d.ts.map
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.quoteIdentifier = quoteIdentifier;
|
|
4
|
+
exports.quoteTableIdentifier = quoteTableIdentifier;
|
|
5
|
+
exports.sanitizeParamName = sanitizeParamName;
|
|
6
|
+
exports.normalizeSortDirection = normalizeSortDirection;
|
|
7
|
+
exports.toNonNegativeInt = toNonNegativeInt;
|
|
8
|
+
exports.buildOrderBy = buildOrderBy;
|
|
9
|
+
exports.parseWhere = parseWhere;
|
|
10
|
+
/**
|
|
11
|
+
* SQL Server identifier quoting.
|
|
12
|
+
*
|
|
13
|
+
* Wraps a bare identifier in square brackets and escapes any embedded
|
|
14
|
+
* closing bracket (`]` → `]]`), which is the only character that can break
|
|
15
|
+
* out of a bracketed identifier in T-SQL. This blocks SQL injection through
|
|
16
|
+
* user-supplied column / alias names.
|
|
17
|
+
*/
|
|
18
|
+
function quoteIdentifier(name) {
|
|
19
|
+
const s = String(name);
|
|
20
|
+
return `[${s.replace(/\]/g, "]]")}]`;
|
|
21
|
+
}
|
|
22
|
+
function unquoteBracketIdentifier(part) {
|
|
23
|
+
const trimmed = String(part).trim();
|
|
24
|
+
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
25
|
+
return trimmed.slice(1, -1).replace(/\]\]/g, "]");
|
|
26
|
+
}
|
|
27
|
+
return trimmed;
|
|
28
|
+
}
|
|
29
|
+
function splitMultipartIdentifier(name) {
|
|
30
|
+
const parts = [];
|
|
31
|
+
let current = "";
|
|
32
|
+
let inBracket = false;
|
|
33
|
+
const input = String(name).trim();
|
|
34
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
35
|
+
const ch = input[i];
|
|
36
|
+
if (ch === "[")
|
|
37
|
+
inBracket = true;
|
|
38
|
+
if (ch === "]") {
|
|
39
|
+
if (input[i + 1] === "]") {
|
|
40
|
+
current += "]]";
|
|
41
|
+
i += 1;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
inBracket = false;
|
|
45
|
+
}
|
|
46
|
+
if (ch === "." && !inBracket) {
|
|
47
|
+
if (current.trim())
|
|
48
|
+
parts.push(current.trim());
|
|
49
|
+
current = "";
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
current += ch;
|
|
53
|
+
}
|
|
54
|
+
if (current.trim())
|
|
55
|
+
parts.push(current.trim());
|
|
56
|
+
return parts.length > 0 ? parts : [input];
|
|
57
|
+
}
|
|
58
|
+
/** Quotes one-part or multipart SQL Server table identifiers safely. */
|
|
59
|
+
function quoteTableIdentifier(name) {
|
|
60
|
+
return splitMultipartIdentifier(name)
|
|
61
|
+
.map(part => quoteIdentifier(unquoteBracketIdentifier(part)))
|
|
62
|
+
.join(".");
|
|
63
|
+
}
|
|
64
|
+
/** Sanitizes a parameter name to a safe T-SQL identifier fragment. */
|
|
65
|
+
function sanitizeParamName(name) {
|
|
66
|
+
const s = String(name);
|
|
67
|
+
const cleaned = s.replace(/[^A-Za-z0-9_]/g, "_");
|
|
68
|
+
return /^[A-Za-z]/.test(cleaned) ? cleaned : `p_${cleaned}`;
|
|
69
|
+
}
|
|
70
|
+
/** Validates a direction token, defaulting to ASC. */
|
|
71
|
+
function normalizeSortDirection(dir) {
|
|
72
|
+
const upper = typeof dir === "string" ? dir.toUpperCase() : "";
|
|
73
|
+
return upper === "DESC" ? "DESC" : "ASC";
|
|
74
|
+
}
|
|
75
|
+
/** Coerces a value to a non-negative integer, or returns the fallback. */
|
|
76
|
+
function toNonNegativeInt(value, fallback = 0) {
|
|
77
|
+
const n = Number.parseInt(String(value), 10);
|
|
78
|
+
return Number.isFinite(n) && n >= 0 ? n : fallback;
|
|
79
|
+
}
|
|
80
|
+
const OPERATOR_KEYS = ["equals", "in", "notIn", "contains", "startsWith", "endsWith", "not", "gte", "lte", "gt", "lt"];
|
|
81
|
+
function isOperatorValue(value) {
|
|
82
|
+
if (!value || typeof value !== "object" || value instanceof Date)
|
|
83
|
+
return false;
|
|
84
|
+
return OPERATOR_KEYS.some((op) => op in value);
|
|
85
|
+
}
|
|
86
|
+
function buildOrderBy(orderBy) {
|
|
87
|
+
if (!orderBy)
|
|
88
|
+
return "";
|
|
89
|
+
const orderClauses = [];
|
|
90
|
+
const orderByArr = Array.isArray(orderBy) ? orderBy : [orderBy];
|
|
91
|
+
for (const orderObj of orderByArr) {
|
|
92
|
+
if (orderObj && typeof orderObj === "object") {
|
|
93
|
+
for (const [k, dir] of Object.entries(orderObj)) {
|
|
94
|
+
orderClauses.push(`${quoteIdentifier(k)} ${normalizeSortDirection(dir)}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return orderClauses.length > 0 ? ` ORDER BY ${orderClauses.join(", ")}` : "";
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Recursively builds a WHERE clause from a structured filter object.
|
|
102
|
+
*
|
|
103
|
+
* Values are always bound as parameters (`@name`); only identifiers are
|
|
104
|
+
* interpolated directly, and they are run through {@link quoteIdentifier}.
|
|
105
|
+
*/
|
|
106
|
+
function parseWhere(modelName, where, params, prefix = "", ctx = {}) {
|
|
107
|
+
if (!where)
|
|
108
|
+
return "";
|
|
109
|
+
const conditions = [];
|
|
110
|
+
const relationMap = ctx.relationMap || {};
|
|
111
|
+
const modelToTable = ctx.modelToTable || {};
|
|
112
|
+
const cleanWhere = {};
|
|
113
|
+
for (const [key, value] of Object.entries(where)) {
|
|
114
|
+
if (key.includes("_") && value && typeof value === "object" && !(value instanceof Date) && !isOperatorValue(value)) {
|
|
115
|
+
Object.assign(cleanWhere, value);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
cleanWhere[key] = value;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
for (const [key, value] of Object.entries(cleanWhere)) {
|
|
122
|
+
if (key === "OR" && Array.isArray(value)) {
|
|
123
|
+
const orConditions = value.map((subWhere, idx) => parseWhere(modelName, subWhere, params, `${prefix}or_${idx}_`, ctx));
|
|
124
|
+
const filtered = orConditions.filter(Boolean);
|
|
125
|
+
if (filtered.length > 0) {
|
|
126
|
+
conditions.push(`(${filtered.join(" OR ")})`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else if (key === "AND") {
|
|
130
|
+
const andItems = Array.isArray(value) ? value : [value];
|
|
131
|
+
const andConditions = andItems.map((subWhere, idx) => parseWhere(modelName, subWhere, params, `${prefix}and_${idx}_`, ctx));
|
|
132
|
+
const filtered = andConditions.filter(Boolean);
|
|
133
|
+
if (filtered.length > 0) {
|
|
134
|
+
conditions.push(`(${filtered.join(" AND ")})`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else if (key === "NOT") {
|
|
138
|
+
const notItems = Array.isArray(value) ? value : [value];
|
|
139
|
+
const notConditions = notItems.map((subWhere, idx) => parseWhere(modelName, subWhere, params, `${prefix}not_${idx}_`, ctx));
|
|
140
|
+
const filtered = notConditions.filter(Boolean);
|
|
141
|
+
if (filtered.length > 0) {
|
|
142
|
+
conditions.push(`NOT (${filtered.join(" AND ")})`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
const modelRelations = relationMap[modelName];
|
|
147
|
+
const relation = modelRelations?.[key];
|
|
148
|
+
if (relation) {
|
|
149
|
+
const relationTable = quoteTableIdentifier(modelToTable[relation.modelName] || relation.modelName);
|
|
150
|
+
const subParams = {};
|
|
151
|
+
let subWhere = value;
|
|
152
|
+
let op = "some";
|
|
153
|
+
if (value && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date)) {
|
|
154
|
+
if (value.some) {
|
|
155
|
+
subWhere = value.some;
|
|
156
|
+
op = "some";
|
|
157
|
+
}
|
|
158
|
+
else if (value.none) {
|
|
159
|
+
subWhere = value.none;
|
|
160
|
+
op = "none";
|
|
161
|
+
}
|
|
162
|
+
else if (value.every) {
|
|
163
|
+
subWhere = value.every;
|
|
164
|
+
op = "every";
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const subWhereSql = parseWhere(relation.modelName, subWhere, subParams, `${prefix}${key}_`, ctx);
|
|
168
|
+
Object.assign(params, subParams);
|
|
169
|
+
const fk = quoteIdentifier(relation.foreignKey);
|
|
170
|
+
const lk = quoteIdentifier(relation.localKey);
|
|
171
|
+
const outerKey = relation.relationType === "one" ? fk : lk;
|
|
172
|
+
const innerKey = relation.relationType === "one" ? lk : fk;
|
|
173
|
+
if (op === "every") {
|
|
174
|
+
if (subWhereSql) {
|
|
175
|
+
conditions.push(`${outerKey} NOT IN (SELECT ${innerKey} FROM ${relationTable} WITH (NOLOCK) WHERE NOT (${subWhereSql}))`);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
conditions.push("1 = 1");
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
const relationPredicate = subWhereSql ? ` WHERE ${subWhereSql}` : "";
|
|
183
|
+
const operator = op === "none" ? "NOT IN" : "IN";
|
|
184
|
+
conditions.push(`${outerKey} ${operator} (SELECT ${innerKey} FROM ${relationTable} WITH (NOLOCK)${relationPredicate})`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
const col = quoteIdentifier(key);
|
|
189
|
+
const paramName = sanitizeParamName(`${prefix}${key}`);
|
|
190
|
+
if (value && typeof value === "object" && !(value instanceof Date)) {
|
|
191
|
+
const ops = Object.entries(value);
|
|
192
|
+
for (const [op, opVal] of ops) {
|
|
193
|
+
if (op === "equals") {
|
|
194
|
+
if (opVal === null) {
|
|
195
|
+
conditions.push(`${col} IS NULL`);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
conditions.push(`${col} = @${paramName}_equals`);
|
|
199
|
+
params[`${paramName}_equals`] = opVal;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
else if (op === "in" && Array.isArray(opVal)) {
|
|
203
|
+
if (opVal.length === 0) {
|
|
204
|
+
conditions.push("1 = 0");
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
const inParams = [];
|
|
208
|
+
opVal.forEach((item, idx) => {
|
|
209
|
+
const inParamName = `${paramName}_in_${idx}`;
|
|
210
|
+
inParams.push(`@${inParamName}`);
|
|
211
|
+
params[inParamName] = item;
|
|
212
|
+
});
|
|
213
|
+
conditions.push(`${col} IN (${inParams.join(", ")})`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
else if (op === "notIn" && Array.isArray(opVal)) {
|
|
217
|
+
if (opVal.length === 0) {
|
|
218
|
+
conditions.push("1 = 1");
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
const inParams = [];
|
|
222
|
+
opVal.forEach((item, idx) => {
|
|
223
|
+
const inParamName = `${paramName}_notin_${idx}`;
|
|
224
|
+
inParams.push(`@${inParamName}`);
|
|
225
|
+
params[inParamName] = item;
|
|
226
|
+
});
|
|
227
|
+
conditions.push(`${col} NOT IN (${inParams.join(", ")})`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
else if (op === "contains") {
|
|
231
|
+
conditions.push(`${col} LIKE @${paramName}_contains`);
|
|
232
|
+
params[`${paramName}_contains`] = `%${opVal}%`;
|
|
233
|
+
}
|
|
234
|
+
else if (op === "startsWith") {
|
|
235
|
+
conditions.push(`${col} LIKE @${paramName}_startsWith`);
|
|
236
|
+
params[`${paramName}_startsWith`] = `${opVal}%`;
|
|
237
|
+
}
|
|
238
|
+
else if (op === "endsWith") {
|
|
239
|
+
conditions.push(`${col} LIKE @${paramName}_endsWith`);
|
|
240
|
+
params[`${paramName}_endsWith`] = `%${opVal}`;
|
|
241
|
+
}
|
|
242
|
+
else if (op === "not") {
|
|
243
|
+
if (opVal === null) {
|
|
244
|
+
conditions.push(`${col} IS NOT NULL`);
|
|
245
|
+
}
|
|
246
|
+
else if (opVal && typeof opVal === "object" && !(opVal instanceof Date) && !Array.isArray(opVal)) {
|
|
247
|
+
const nestedParams = {};
|
|
248
|
+
const nestedSql = parseWhere(modelName, { [key]: opVal }, nestedParams, `${prefix}${key}_not_`, ctx);
|
|
249
|
+
Object.assign(params, nestedParams);
|
|
250
|
+
if (nestedSql)
|
|
251
|
+
conditions.push(`NOT (${nestedSql})`);
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
conditions.push(`${col} <> @${paramName}_not`);
|
|
255
|
+
params[`${paramName}_not`] = opVal;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
else if (op === "gte") {
|
|
259
|
+
conditions.push(`${col} >= @${paramName}_gte`);
|
|
260
|
+
params[`${paramName}_gte`] = opVal;
|
|
261
|
+
}
|
|
262
|
+
else if (op === "lte") {
|
|
263
|
+
conditions.push(`${col} <= @${paramName}_lte`);
|
|
264
|
+
params[`${paramName}_lte`] = opVal;
|
|
265
|
+
}
|
|
266
|
+
else if (op === "gt") {
|
|
267
|
+
conditions.push(`${col} > @${paramName}_gt`);
|
|
268
|
+
params[`${paramName}_gt`] = opVal;
|
|
269
|
+
}
|
|
270
|
+
else if (op === "lt") {
|
|
271
|
+
conditions.push(`${col} < @${paramName}_lt`);
|
|
272
|
+
params[`${paramName}_lt`] = opVal;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
if (value === null) {
|
|
278
|
+
conditions.push(`${col} IS NULL`);
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
conditions.push(`${col} = @${paramName}`);
|
|
282
|
+
params[paramName] = value;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return conditions.join(" AND ");
|
|
289
|
+
}
|
|
290
|
+
//# sourceMappingURL=sql-utils.js.map
|
|
@@ -19,15 +19,11 @@ export class CodeGenerator {
|
|
|
19
19
|
|
|
20
20
|
private generateBaseTs() {
|
|
21
21
|
const baseTs = `
|
|
22
|
+
import { An5ClientKnownRequestError as An5KnownError } from "@an5/orm";
|
|
23
|
+
|
|
22
24
|
export namespace An5 {
|
|
23
|
-
export
|
|
24
|
-
|
|
25
|
-
meta?: any;
|
|
26
|
-
constructor(message: string, { code, clientVersion }: { code: string; clientVersion: string }) {
|
|
27
|
-
super(message);
|
|
28
|
-
this.code = code;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
25
|
+
export const An5ClientKnownRequestError: typeof An5KnownError = An5KnownError;
|
|
26
|
+
export type An5ClientKnownRequestError = An5KnownError;
|
|
31
27
|
export type SortOrder = 'asc' | 'desc';
|
|
32
28
|
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; };
|
|
33
29
|
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,11 +33,11 @@ export namespace An5 {
|
|
|
37
33
|
export type NumberNullableFilter = { equals?: number | null; in?: (number | null)[]; notIn?: (number | null)[]; lt?: number; lte?: number; gt?: number; gte?: number; not?: number | NumberNullableFilter | null; };
|
|
38
34
|
export type BooleanFilter = { equals?: boolean; not?: boolean | BooleanFilter; };
|
|
39
35
|
export type BooleanNullableFilter = { equals?: boolean | null; not?: boolean | BooleanNullableFilter | null; };
|
|
40
|
-
export type DateTimeFilter = { equals?: Date; in?: Date[]; notIn?: Date[]; lt?: Date; lte?: Date; gt?: Date; gte?: Date; not?: Date | DateTimeFilter; };
|
|
41
|
-
export type DateTimeNullableFilter = { equals?: Date | null; in?: (Date | null)[]; notIn?: (Date | null)[]; lt?: Date; lte?: Date; gt?: Date; gte?: Date; not?: Date | DateTimeNullableFilter | null; };
|
|
36
|
+
export type DateTimeFilter = { equals?: Date | string; in?: (Date | string)[]; notIn?: (Date | string)[]; lt?: Date | string; lte?: Date | string; gt?: Date | string; gte?: Date | string; not?: Date | string | DateTimeFilter; };
|
|
37
|
+
export type DateTimeNullableFilter = { equals?: Date | string | null; in?: ((Date | string) | null)[]; notIn?: ((Date | string) | null)[]; lt?: Date | string; lte?: Date | string; gt?: Date | string; gte?: Date | string; not?: Date | string | DateTimeNullableFilter | null; };
|
|
42
38
|
}
|
|
43
39
|
|
|
44
|
-
export interface TableClient<T, WhereInput = any,
|
|
40
|
+
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, AggregateArgs = any, GroupByArgs = any> {
|
|
45
41
|
findMany(args?: FindManyArgs): Promise<T[]>;
|
|
46
42
|
vectorSearch(args: { vector: number[]; take?: number; where?: WhereInput; include?: Include; vectorField?: string; distanceMetric?: 'cosine' | 'euclidean' | 'dot'; }): Promise<(T & { distance: number })[]>;
|
|
47
43
|
findFirst(args?: FindFirstArgs): Promise<T | null>;
|
|
@@ -53,8 +49,8 @@ export interface TableClient<T, WhereInput = any, Select = any, Include = any, C
|
|
|
53
49
|
updateMany(args: { where?: WhereInput; data: UpdateInput; }): Promise<{ count: number }>;
|
|
54
50
|
delete(args: DeleteArgs): Promise<T>;
|
|
55
51
|
deleteMany(args?: { where?: WhereInput; }): Promise<{ count: number }>;
|
|
56
|
-
aggregate(args:
|
|
57
|
-
groupBy(args:
|
|
52
|
+
aggregate(args: AggregateArgs): Promise<any>;
|
|
53
|
+
groupBy(args: GroupByArgs): Promise<any[]>;
|
|
58
54
|
upsert(args: UpsertArgs): Promise<T>;
|
|
59
55
|
}
|
|
60
56
|
`;
|
|
@@ -88,7 +84,7 @@ export interface TableClient<T, WhereInput = any, Select = any, Include = any, C
|
|
|
88
84
|
content += ` NOT?: ${model.name}WhereInput | ${model.name}WhereInput[];\n`;
|
|
89
85
|
for (const field of model.fields) {
|
|
90
86
|
const filter = this.getFieldFilterType(field);
|
|
91
|
-
content += ` ${field.name}?: ${field.type} | ${filter}${field.isOptional ? ' | null' : ''};\n`;
|
|
87
|
+
content += ` ${field.name}?: ${this.inputType(field.type)} | ${filter}${field.isOptional ? ' | null' : ''};\n`;
|
|
92
88
|
}
|
|
93
89
|
for (const rel of model.relations) {
|
|
94
90
|
if (rel.isArray) {
|
|
@@ -118,18 +114,25 @@ export interface TableClient<T, WhereInput = any, Select = any, Include = any, C
|
|
|
118
114
|
}
|
|
119
115
|
content += `export type ${model.name}Include = { ${includeRels.join('; ')}${includeRels.length > 0 ? ';' : ''} };\n`;
|
|
120
116
|
|
|
121
|
-
content += `export type ${model.name}CreateInput = { ${model.fields.map(f => `${f.name}${f.isOptional || f.hasDefault ? '?' : ''}: ${f.type}${f.isOptional ? ' | null' : ''}`).join('; ')}; ${model.relations.map(r => `${r.name}?: { create?: ${r.type}CreateInput | ${r.type}CreateInput[]; connect?: ${r.type}WhereInput | ${r.type}WhereInput[]; }`).join('; ')} };\n`;
|
|
122
|
-
content += `export type ${model.name}UpdateInput = { ${model.fields.filter(f => !f.isId).map(f => `${f.name}?: ${f.type === 'number' ? 'number | An5.IntFieldUpdateOperationsInput' : f.type}${f.isOptional ? ' | null' : ''}`).join('; ')}; ${model.relations.map(r => `${r.name}?: { create?: ${r.type}CreateInput | ${r.type}CreateInput[]; connect?: ${r.type}WhereInput | ${r.type}WhereInput[]; set?: ${r.type}WhereInput | ${r.type}WhereInput[]; disconnect?: ${r.type}WhereInput | ${r.type}WhereInput[]; delete?: ${r.type}WhereInput | ${r.type}WhereInput[]; update?: { where: ${r.type}WhereInput; data: ${r.type}UpdateInput; } | { where: ${r.type}WhereInput; data: ${r.type}UpdateInput; }[]; upsert?: { where: ${r.type}WhereInput; create: ${r.type}CreateInput; update: ${r.type}UpdateInput; } | { where: ${r.type}WhereInput; create: ${r.type}CreateInput; update: ${r.type}UpdateInput; }[]; }`).join('; ')} };\n`;
|
|
117
|
+
content += `export type ${model.name}CreateInput = { ${model.fields.map(f => `${f.name}${f.isOptional || f.hasDefault ? '?' : ''}: ${this.inputType(f.type)}${f.isOptional ? ' | null' : ''}`).join('; ')}; ${model.relations.map(r => `${r.name}?: { create?: ${r.type}CreateInput | ${r.type}CreateInput[]; connect?: ${r.type}WhereInput | ${r.type}WhereInput[]; }`).join('; ')} };\n`;
|
|
118
|
+
content += `export type ${model.name}UpdateInput = { ${model.fields.filter(f => !f.isId).map(f => `${f.name}?: ${f.type === 'number' ? 'number | An5.IntFieldUpdateOperationsInput' : this.inputType(f.type)}${f.isOptional ? ' | null' : ''}`).join('; ')}; ${model.relations.map(r => `${r.name}?: { create?: ${r.type}CreateInput | ${r.type}CreateInput[]; connect?: ${r.type}WhereInput | ${r.type}WhereInput[]; set?: ${r.type}WhereInput | ${r.type}WhereInput[]; disconnect?: ${r.type}WhereInput | ${r.type}WhereInput[]; delete?: ${r.type}WhereInput | ${r.type}WhereInput[]; update?: { where: ${r.type}WhereInput; data: ${r.type}UpdateInput; } | { where: ${r.type}WhereInput; data: ${r.type}UpdateInput; }[]; upsert?: { where: ${r.type}WhereInput; create: ${r.type}CreateInput; update: ${r.type}UpdateInput; } | { where: ${r.type}WhereInput; create: ${r.type}CreateInput; update: ${r.type}UpdateInput; }[]; }`).join('; ')} };\n`;
|
|
123
119
|
|
|
124
|
-
content += `export type ${model.name}
|
|
125
|
-
content += `export type ${model.name}
|
|
120
|
+
content += `export type ${model.name}OrderByInput = { ${model.fields.map(f => `${f.name}?: An5.SortOrder`).join('; ')} };\n`;
|
|
121
|
+
content += `export type ${model.name}FindManyArgs = { where?: ${model.name}WhereInput; orderBy?: ${model.name}OrderByInput | ${model.name}OrderByInput[]; take?: number; skip?: number; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
|
|
122
|
+
content += `export type ${model.name}FindFirstArgs = { where?: ${model.name}WhereInput; orderBy?: ${model.name}OrderByInput | ${model.name}OrderByInput[]; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
|
|
126
123
|
content += `export type ${model.name}FindUniqueArgs = { where?: ${model.name}WhereInput; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
|
|
127
124
|
content += `export type ${model.name}CreateArgs = { data: ${model.name}CreateInput; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
|
|
128
125
|
content += `export type ${model.name}UpdateArgs = { where: ${model.name}WhereInput; data: ${model.name}UpdateInput; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
|
|
129
126
|
content += `export type ${model.name}UpsertArgs = { where: ${model.name}WhereInput; create: ${model.name}CreateInput; update: ${model.name}UpdateInput; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
|
|
130
127
|
content += `export type ${model.name}DeleteArgs = { where: ${model.name}WhereInput; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
|
|
128
|
+
content += `export type ${model.name}ScalarFieldEnum = ${model.fields.map(f => `'${f.name}'`).join(' | ') || 'never'};\n`;
|
|
129
|
+
content += `export type ${model.name}AggregateArgs = { where?: ${model.name}WhereInput; _count?: true | { _all?: true; ${model.fields.map(f => `${f.name}?: true`).join('; ')} }; _sum?: { ${model.fields.filter(f => this.normalizeType(f.type) === 'number').map(f => `${f.name}?: true`).join('; ')} }; _avg?: { ${model.fields.filter(f => this.normalizeType(f.type) === 'number').map(f => `${f.name}?: true`).join('; ')} }; _min?: { ${model.fields.map(f => `${f.name}?: true`).join('; ')} }; _max?: { ${model.fields.map(f => `${f.name}?: true`).join('; ')} }; };\n`;
|
|
130
|
+
const numericHavingFields = model.fields.filter(f => this.normalizeType(f.type) === 'number').map(f => `${f.name}?: An5.NumberFilter | number`).join('; ');
|
|
131
|
+
const scalarHavingFields = model.fields.map(f => `${f.name}?: any`).join('; ');
|
|
132
|
+
content += `export type ${model.name}AggregateHavingInput = { _count?: { _all?: An5.NumberFilter | number; ${model.fields.map(f => `${f.name}?: An5.NumberFilter | number`).join('; ')} }; _sum?: { ${numericHavingFields} }; _avg?: { ${numericHavingFields} }; _min?: { ${scalarHavingFields} }; _max?: { ${scalarHavingFields} }; };\n`;
|
|
133
|
+
content += `export type ${model.name}GroupByArgs = { by: ${model.name}ScalarFieldEnum | ${model.name}ScalarFieldEnum[]; where?: ${model.name}WhereInput; having?: ${model.name}AggregateHavingInput; orderBy?: ${model.name}OrderByInput | ${model.name}OrderByInput[]; skip?: number; take?: number; _count?: true | { _all?: true; ${model.fields.map(f => `${f.name}?: true`).join('; ')} }; _sum?: { ${model.fields.filter(f => this.normalizeType(f.type) === 'number').map(f => `${f.name}?: true`).join('; ')} }; _avg?: { ${model.fields.filter(f => this.normalizeType(f.type) === 'number').map(f => `${f.name}?: true`).join('; ')} }; _min?: { ${model.fields.map(f => `${f.name}?: true`).join('; ')} }; _max?: { ${model.fields.map(f => `${f.name}?: true`).join('; ')} }; };\n`;
|
|
131
134
|
|
|
132
|
-
content += `export type ${model.name}TableClient = TableClient<\n ${model.name},\n ${model.name}WhereInput,\n ${model.name}Select,\n ${model.name}Include,\n ${model.name}CreateInput,\n ${model.name}UpdateInput,\n ${model.name}FindManyArgs,\n ${model.name}FindFirstArgs,\n ${model.name}FindUniqueArgs,\n ${model.name}CreateArgs,\n ${model.name}UpdateArgs,\n ${model.name}UpsertArgs,\n ${model.name}DeleteArgs\n>;\n`;
|
|
135
|
+
content += `export type ${model.name}TableClient = TableClient<\n ${model.name},\n ${model.name}WhereInput,\n ${model.name}Select,\n ${model.name}Include,\n ${model.name}CreateInput,\n ${model.name}UpdateInput,\n ${model.name}FindManyArgs,\n ${model.name}FindFirstArgs,\n ${model.name}FindUniqueArgs,\n ${model.name}CreateArgs,\n ${model.name}UpdateArgs,\n ${model.name}UpsertArgs,\n ${model.name}DeleteArgs,\n ${model.name}AggregateArgs,\n ${model.name}GroupByArgs\n>;\n`;
|
|
133
136
|
|
|
134
137
|
fs.writeFileSync(path.join(this.outputDir, `${model.name}.ts`), content);
|
|
135
138
|
}
|
|
@@ -165,10 +168,22 @@ export interface TableClient<T, WhereInput = any, Select = any, Include = any, C
|
|
|
165
168
|
content += ` export type ${model.name}Include = ${model.name}Types.${model.name}Include;\n`;
|
|
166
169
|
content += ` export type ${model.name}CreateInput = ${model.name}Types.${model.name}CreateInput;\n`;
|
|
167
170
|
content += ` export type ${model.name}UpdateInput = ${model.name}Types.${model.name}UpdateInput;\n`;
|
|
171
|
+
content += ` export type ${model.name}OrderByInput = ${model.name}Types.${model.name}OrderByInput;\n`;
|
|
172
|
+
content += ` export type ${model.name}FindManyArgs = ${model.name}Types.${model.name}FindManyArgs;\n`;
|
|
173
|
+
content += ` export type ${model.name}FindFirstArgs = ${model.name}Types.${model.name}FindFirstArgs;\n`;
|
|
174
|
+
content += ` export type ${model.name}FindUniqueArgs = ${model.name}Types.${model.name}FindUniqueArgs;\n`;
|
|
175
|
+
content += ` export type ${model.name}CreateArgs = ${model.name}Types.${model.name}CreateArgs;\n`;
|
|
176
|
+
content += ` export type ${model.name}UpdateArgs = ${model.name}Types.${model.name}UpdateArgs;\n`;
|
|
177
|
+
content += ` export type ${model.name}UpsertArgs = ${model.name}Types.${model.name}UpsertArgs;\n`;
|
|
178
|
+
content += ` export type ${model.name}DeleteArgs = ${model.name}Types.${model.name}DeleteArgs;\n`;
|
|
179
|
+
content += ` export type ${model.name}TableClient = ${model.name}Types.${model.name}TableClient;\n`;
|
|
180
|
+
content += ` export type ${model.name}ScalarFieldEnum = ${model.name}Types.${model.name}ScalarFieldEnum;\n`;
|
|
181
|
+
content += ` export type ${model.name}AggregateArgs = ${model.name}Types.${model.name}AggregateArgs;\n`;
|
|
182
|
+
content += ` export type ${model.name}GroupByArgs = ${model.name}Types.${model.name}GroupByArgs;\n`;
|
|
168
183
|
}
|
|
169
184
|
content += `}\n`;
|
|
170
185
|
|
|
171
|
-
content += `\nexport class An5Client {\n $connect(): Promise<void> { return Promise.resolve(); }\n $disconnect(): Promise<void> { return Promise.resolve(); }\n $transaction<R>(fn: (tx: An5Client) => Promise<R>, options?: { timeout?: number }): Promise<R>;\n $transaction<R>(list: Promise<R>[]): Promise<R[]>;\n $transaction(fn: any,
|
|
186
|
+
content += `\nexport class An5Client {\n $connect(): Promise<void> { return Promise.resolve(); }\n $disconnect(): Promise<void> { return Promise.resolve(); }\n $transaction<R>(fn: (tx: An5Client) => Promise<R>, options?: { timeout?: number }): Promise<R>;\n $transaction<R>(list: Promise<R>[]): Promise<R[]>;\n $transaction(fn: any, _options?: any): Promise<any> { return typeof fn === 'function' ? fn(this) : Promise.all(fn); }\n $begin(): Promise<An5Client> { return Promise.resolve(this); }\n $commit(): Promise<void> { return Promise.resolve(); }\n $rollback(): Promise<void> { return Promise.resolve(); }\n $queryRaw<T = any>(_queryParts: TemplateStringsArray | string, ..._values: any[]): Promise<T> { return Promise.resolve([] as any); }\n $queryRawUnsafe<R = any>(_query: string, ..._values: any[]): Promise<R> { return Promise.resolve([] as any); }\n $executeRaw<_T = any>(_queryParts: TemplateStringsArray | string, ..._values: any[]): Promise<any> { return Promise.resolve(0 as any); }\n $executeRawUnsafe(_query: string, ..._values: any[]): Promise<number> { return Promise.resolve(0); }\n`;
|
|
172
187
|
|
|
173
188
|
const addedProps = new Set<string>();
|
|
174
189
|
for (const model of models) {
|
|
@@ -187,12 +202,16 @@ export interface TableClient<T, WhereInput = any, Select = any, Include = any, C
|
|
|
187
202
|
|
|
188
203
|
private getAllPropertyVariations(modelName: string): string[] {
|
|
189
204
|
const variations = new Set<string>();
|
|
205
|
+
variations.add(modelName);
|
|
190
206
|
variations.add(this.toCamelCase(modelName));
|
|
207
|
+
variations.add(modelName + 's');
|
|
208
|
+
variations.add(this.toCamelCase(modelName) + 's');
|
|
191
209
|
const acronyms = ['LLM', 'AI', 'MCP', 'IT', 'QC', 'HR', 'MR', 'WH', 'SSIS', 'API', 'URL', 'ID', 'JSON'];
|
|
192
210
|
for (const acronym of acronyms) {
|
|
193
211
|
if (modelName.startsWith(acronym)) {
|
|
194
212
|
variations.add(acronym.toLowerCase() + modelName.slice(acronym.length));
|
|
195
213
|
variations.add(acronym[0].toLowerCase() + acronym.slice(1) + modelName.slice(acronym.length));
|
|
214
|
+
variations.add(acronym.toLowerCase() + modelName.slice(acronym.length) + 's');
|
|
196
215
|
}
|
|
197
216
|
}
|
|
198
217
|
return Array.from(variations);
|
|
@@ -207,6 +226,12 @@ export interface TableClient<T, WhereInput = any, Select = any, Include = any, C
|
|
|
207
226
|
return 'any';
|
|
208
227
|
}
|
|
209
228
|
|
|
229
|
+
// Input positions (create/update/where scalars) accept ISO strings as well
|
|
230
|
+
// as Date objects — Prisma parity for DateTime fields.
|
|
231
|
+
private inputType(tsType: string): string {
|
|
232
|
+
return tsType === 'Date' ? 'Date | string' : tsType;
|
|
233
|
+
}
|
|
234
|
+
|
|
210
235
|
private normalizeType(type: string): string {
|
|
211
236
|
const lowerType = type.toLowerCase();
|
|
212
237
|
if (['string', 'varchar', 'nvarchar', 'char', 'nchar', 'text', 'ntext', 'xml', 'guid', 'uuid', 'uniqueidentifier', 'sysname', 'hierarchyid', 'geography', 'geometry', 'sql_variant', 'sqlvariant', 'rowversion', 'variant'].includes(lowerType)) {
|