forge-sql-orm 1.1.31 → 2.0.1
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 +189 -43
- package/dist/ForgeSQLORM.js +81 -161
- package/dist/ForgeSQLORM.js.map +1 -1
- package/dist/ForgeSQLORM.mjs +68 -148
- package/dist/ForgeSQLORM.mjs.map +1 -1
- package/dist/core/ForgeSQLCrudOperations.d.ts.map +1 -1
- package/dist/core/ForgeSQLORM.d.ts +1 -2
- package/dist/core/ForgeSQLORM.d.ts.map +1 -1
- package/dist/core/ForgeSQLQueryBuilder.d.ts +6 -8
- package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
- package/dist/core/ForgeSQLSelectOperations.d.ts +0 -38
- package/dist/core/ForgeSQLSelectOperations.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/utils/forgeDriver.d.ts +3 -0
- package/dist/utils/forgeDriver.d.ts.map +1 -0
- package/dist/utils/sqlUtils.d.ts +2 -2
- package/dist/utils/sqlUtils.d.ts.map +1 -1
- package/dist-cli/cli.js +28 -38
- package/dist-cli/cli.js.map +1 -1
- package/dist-cli/cli.mjs +24 -34
- package/dist-cli/cli.mjs.map +1 -1
- package/package.json +8 -10
- package/src/core/ForgeSQLCrudOperations.ts +11 -19
- package/src/core/ForgeSQLORM.ts +7 -8
- package/src/core/ForgeSQLQueryBuilder.ts +6 -11
- package/src/core/ForgeSQLSelectOperations.ts +4 -132
- package/src/index.ts +1 -0
- package/src/utils/forgeDriver.ts +37 -0
- package/src/utils/sqlUtils.ts +48 -38
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import { sql, UpdateQueryResponse } from "@forge/sql";
|
|
2
|
-
import { extractAlias, parseDateTime } from "../utils/sqlUtils";
|
|
3
2
|
import { ForgeSqlOrmOptions, SchemaSqlForgeSql } from "./ForgeSQLQueryBuilder";
|
|
4
|
-
import { AnyMySqlSelect } from "drizzle-orm/mysql-core";
|
|
5
3
|
import {
|
|
6
4
|
AnyMySqlSelectQueryBuilder,
|
|
7
5
|
MySqlSelectDynamic,
|
|
8
6
|
} from "drizzle-orm/mysql-core/query-builders/select.types";
|
|
9
|
-
import { Column, SQL, SQLChunk, StringChunk } from "drizzle-orm";
|
|
10
7
|
|
|
11
8
|
/**
|
|
12
9
|
* Class implementing SQL select operations for ForgeSQL ORM.
|
|
@@ -23,134 +20,6 @@ export class ForgeSQLSelectOperations implements SchemaSqlForgeSql {
|
|
|
23
20
|
this.options = options;
|
|
24
21
|
}
|
|
25
22
|
|
|
26
|
-
/**
|
|
27
|
-
* Executes a Drizzle query and returns the results.
|
|
28
|
-
* Maps the raw database results to the appropriate entity types.
|
|
29
|
-
*
|
|
30
|
-
* @template T - The type of the query builder
|
|
31
|
-
* @param {T} query - The Drizzle query to execute
|
|
32
|
-
* @returns {Promise<Awaited<T>>} The query results mapped to entity types
|
|
33
|
-
*/
|
|
34
|
-
async executeQuery<T extends MySqlSelectDynamic<AnyMySqlSelectQueryBuilder>>(
|
|
35
|
-
query: T,
|
|
36
|
-
): Promise<Awaited<T>> {
|
|
37
|
-
const queryType = <AnyMySqlSelect>query;
|
|
38
|
-
const querySql = queryType.toSQL();
|
|
39
|
-
const datas = await this.executeRawSQL<unknown>(querySql.sql, querySql.params);
|
|
40
|
-
|
|
41
|
-
if (!datas.length) return [] as Awaited<T>;
|
|
42
|
-
|
|
43
|
-
return datas.map((r) => {
|
|
44
|
-
const rawModel = r as Record<string, unknown>;
|
|
45
|
-
const newModel: any = {};
|
|
46
|
-
|
|
47
|
-
// @ts-ignore - Drizzle internal property
|
|
48
|
-
const columns = queryType.config.fields;
|
|
49
|
-
|
|
50
|
-
Object.entries(columns).forEach(([name, column]: [string, any]) => {
|
|
51
|
-
const { realColumn, aliasName } = this.extractColumnInfo(column);
|
|
52
|
-
const value = rawModel[aliasName];
|
|
53
|
-
|
|
54
|
-
if (value === null || value === undefined) {
|
|
55
|
-
newModel[name] = value;
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
newModel[name] = this.parseColumnValue(realColumn, value);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
return newModel;
|
|
63
|
-
}) as Awaited<T>;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Extracts column information and alias name from a column definition.
|
|
68
|
-
* @param {any} column - The column definition from Drizzle
|
|
69
|
-
* @returns {Object} Object containing the real column and its alias name
|
|
70
|
-
*/
|
|
71
|
-
private extractColumnInfo(column: any): { realColumn: Column; aliasName: string } {
|
|
72
|
-
if (column instanceof SQL) {
|
|
73
|
-
const realColumnSql = <SQL>column;
|
|
74
|
-
const realColumn = realColumnSql.queryChunks.find(
|
|
75
|
-
(q: SQLChunk) => q instanceof Column,
|
|
76
|
-
) as Column;
|
|
77
|
-
|
|
78
|
-
let stringChunk = this.findAliasChunk(realColumnSql);
|
|
79
|
-
let withoutAlias = false;
|
|
80
|
-
|
|
81
|
-
if (!realColumn && (!stringChunk || !stringChunk.value || !stringChunk.value?.length)) {
|
|
82
|
-
stringChunk = realColumnSql.queryChunks
|
|
83
|
-
.filter((q: SQLChunk) => q instanceof StringChunk)
|
|
84
|
-
.find((q: SQLChunk) => (q as StringChunk).value[0]) as StringChunk;
|
|
85
|
-
withoutAlias = true;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const aliasName = this.resolveAliasName(stringChunk, realColumn, withoutAlias);
|
|
89
|
-
|
|
90
|
-
return { realColumn, aliasName };
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return { realColumn: column, aliasName: column.name };
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Finds the alias chunk in SQL query chunks.
|
|
98
|
-
* @param {SQL} realColumnSql - The SQL query chunks
|
|
99
|
-
* @returns {StringChunk | undefined} The string chunk containing the alias or undefined
|
|
100
|
-
*/
|
|
101
|
-
private findAliasChunk(realColumnSql: SQL): StringChunk | undefined {
|
|
102
|
-
return realColumnSql.queryChunks
|
|
103
|
-
.filter((q: SQLChunk) => q instanceof StringChunk)
|
|
104
|
-
.find((q: SQLChunk) =>
|
|
105
|
-
(q as StringChunk).value.find((f) => f.toLowerCase().includes("as")),
|
|
106
|
-
) as StringChunk;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Resolves the alias name from the string chunk or column.
|
|
111
|
-
* @param {StringChunk | undefined} stringChunk - The string chunk containing the alias
|
|
112
|
-
* @param {Column | undefined} realColumn - The real column definition
|
|
113
|
-
* @param {boolean} withoutAlias - Whether the column has no alias
|
|
114
|
-
* @returns {string} The resolved alias name
|
|
115
|
-
*/
|
|
116
|
-
private resolveAliasName(
|
|
117
|
-
stringChunk: StringChunk | undefined,
|
|
118
|
-
realColumn: Column | undefined,
|
|
119
|
-
withoutAlias: boolean,
|
|
120
|
-
): string {
|
|
121
|
-
if (stringChunk) {
|
|
122
|
-
if (withoutAlias) {
|
|
123
|
-
return stringChunk.value[0];
|
|
124
|
-
}
|
|
125
|
-
const asClause = stringChunk.value.find((f) => f.toLowerCase().includes("as"));
|
|
126
|
-
return asClause ? extractAlias(asClause.trim()) : realColumn?.name || "";
|
|
127
|
-
}
|
|
128
|
-
return realColumn?.name || "";
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Parses a column value based on its SQL type.
|
|
133
|
-
* Handles datetime, date, and time types with appropriate formatting.
|
|
134
|
-
*
|
|
135
|
-
* @param {Column} column - The column definition
|
|
136
|
-
* @param {unknown} value - The raw value to parse
|
|
137
|
-
* @returns {unknown} The parsed value
|
|
138
|
-
*/
|
|
139
|
-
private parseColumnValue(column: Column, value: unknown): unknown {
|
|
140
|
-
if (!column) return value;
|
|
141
|
-
|
|
142
|
-
switch (column.getSQLType()) {
|
|
143
|
-
case "datetime":
|
|
144
|
-
return parseDateTime(value as string, "YYYY-MM-DDTHH:mm:ss.SSS");
|
|
145
|
-
case "date":
|
|
146
|
-
return parseDateTime(value as string, "YYYY-MM-DD");
|
|
147
|
-
case "time":
|
|
148
|
-
return parseDateTime(value as string, "HH:mm:ss.SSS");
|
|
149
|
-
default:
|
|
150
|
-
return value;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
23
|
/**
|
|
155
24
|
* Executes a Drizzle query and returns a single result.
|
|
156
25
|
* Throws an error if more than one record is returned.
|
|
@@ -165,7 +34,7 @@ export class ForgeSQLSelectOperations implements SchemaSqlForgeSql {
|
|
|
165
34
|
): Promise<
|
|
166
35
|
Awaited<T> extends Array<any> ? Awaited<T>[number] | undefined : Awaited<T> | undefined
|
|
167
36
|
> {
|
|
168
|
-
const results: Awaited<T> = await
|
|
37
|
+
const results: Awaited<T> = await query;
|
|
169
38
|
const datas = results as unknown[];
|
|
170
39
|
if (!datas.length) {
|
|
171
40
|
return undefined;
|
|
@@ -192,6 +61,9 @@ export class ForgeSQLSelectOperations implements SchemaSqlForgeSql {
|
|
|
192
61
|
}
|
|
193
62
|
const sqlStatement = sql.prepare<T>(query);
|
|
194
63
|
if (params) {
|
|
64
|
+
if (this.options.logRawSqlQuery && this.options.logRawSqlQueryParams) {
|
|
65
|
+
console.debug("Executing with SQL Params: " + JSON.stringify(params));
|
|
66
|
+
}
|
|
195
67
|
sqlStatement.bindParams(...params);
|
|
196
68
|
}
|
|
197
69
|
const result = await sqlStatement.execute();
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { sql } from "@forge/sql";
|
|
2
|
+
import {AnyMySql2Connection} from "drizzle-orm/mysql2/driver";
|
|
3
|
+
|
|
4
|
+
interface ForgeSQLResult {
|
|
5
|
+
rows: Record<string, unknown>[] | Record<string, unknown>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const forgeDriver = {
|
|
9
|
+
query: async (query: { sql: string }, params?: unknown[]) => {
|
|
10
|
+
try {
|
|
11
|
+
const sqlStatement = await sql.prepare<unknown>(query.sql);
|
|
12
|
+
if (params) {
|
|
13
|
+
await sqlStatement.bindParams(...params);
|
|
14
|
+
}
|
|
15
|
+
const result = await sqlStatement.execute() as ForgeSQLResult;
|
|
16
|
+
|
|
17
|
+
let rows;
|
|
18
|
+
if (Array.isArray(result.rows)) {
|
|
19
|
+
rows = [
|
|
20
|
+
result.rows.map(r => Object.values(r as Record<string, unknown>))
|
|
21
|
+
];
|
|
22
|
+
} else {
|
|
23
|
+
rows = [
|
|
24
|
+
result.rows as Record<string, unknown>
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return rows;
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error("SQL Error:", JSON.stringify(error));
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
transaction: async (transactionFn: (tx: any) => Promise<void>) => {
|
|
35
|
+
// Implementation will be added later
|
|
36
|
+
},
|
|
37
|
+
} as unknown as AnyMySql2Connection;
|
package/src/utils/sqlUtils.ts
CHANGED
|
@@ -2,7 +2,7 @@ import moment from "moment";
|
|
|
2
2
|
import { AnyColumn } from "drizzle-orm";
|
|
3
3
|
import { AnyMySqlTable } from "drizzle-orm/mysql-core/index";
|
|
4
4
|
import { PrimaryKeyBuilder } from "drizzle-orm/mysql-core/primary-keys";
|
|
5
|
-
import { AnyIndexBuilder
|
|
5
|
+
import { AnyIndexBuilder } from "drizzle-orm/mysql-core/indexes";
|
|
6
6
|
import { CheckBuilder } from "drizzle-orm/mysql-core/checks";
|
|
7
7
|
import { ForeignKeyBuilder } from "drizzle-orm/mysql-core/foreign-keys";
|
|
8
8
|
import { UniqueConstraintBuilder } from "drizzle-orm/mysql-core/unique-constraint";
|
|
@@ -29,6 +29,14 @@ export interface MetadataInfo {
|
|
|
29
29
|
extras: any[];
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Interface for config builder data
|
|
34
|
+
*/
|
|
35
|
+
interface ConfigBuilderData {
|
|
36
|
+
value?: any;
|
|
37
|
+
[key: string]: any;
|
|
38
|
+
}
|
|
39
|
+
|
|
32
40
|
/**
|
|
33
41
|
* Parses a date string into a Date object using the specified format
|
|
34
42
|
* @param value - The date string to parse
|
|
@@ -57,11 +65,9 @@ export function extractAlias(query: string): string {
|
|
|
57
65
|
* Gets primary keys from the schema.
|
|
58
66
|
* @template T - The type of the table schema
|
|
59
67
|
* @param {T} table - The table schema
|
|
60
|
-
* @returns {[string, AnyColumn][]
|
|
68
|
+
* @returns {[string, AnyColumn][]} Array of primary key name and column pairs
|
|
61
69
|
*/
|
|
62
|
-
export function getPrimaryKeys<T extends AnyMySqlTable>(
|
|
63
|
-
table: T,
|
|
64
|
-
): [string, AnyColumn][] | undefined {
|
|
70
|
+
export function getPrimaryKeys<T extends AnyMySqlTable>(table: T): [string, AnyColumn][] {
|
|
65
71
|
const { columns, primaryKeys } = getTableMetadata(table);
|
|
66
72
|
|
|
67
73
|
// First try to find primary keys in columns
|
|
@@ -91,11 +97,10 @@ export function getPrimaryKeys<T extends AnyMySqlTable>(
|
|
|
91
97
|
});
|
|
92
98
|
});
|
|
93
99
|
|
|
94
|
-
|
|
95
|
-
return result.length > 0 ? result : undefined;
|
|
100
|
+
return Array.from(primaryKeyColumns);
|
|
96
101
|
}
|
|
97
102
|
|
|
98
|
-
return
|
|
103
|
+
return [];
|
|
99
104
|
}
|
|
100
105
|
|
|
101
106
|
/**
|
|
@@ -108,7 +113,6 @@ export function getTableMetadata(table: AnyMySqlTable): MetadataInfo {
|
|
|
108
113
|
const nameSymbol = symbols.find((s) => s.toString().includes("Name"));
|
|
109
114
|
const columnsSymbol = symbols.find((s) => s.toString().includes("Columns"));
|
|
110
115
|
const extraSymbol = symbols.find((s) => s.toString().includes("ExtraConfigBuilder"));
|
|
111
|
-
const foreignKeysSymbol = symbols.find((s) => s.toString().includes("MySqlInlineForeignKeys)"));
|
|
112
116
|
|
|
113
117
|
// Initialize builders arrays
|
|
114
118
|
const builders = {
|
|
@@ -119,42 +123,48 @@ export function getTableMetadata(table: AnyMySqlTable): MetadataInfo {
|
|
|
119
123
|
uniqueConstraints: [] as UniqueConstraintBuilder[],
|
|
120
124
|
extras: [] as any[],
|
|
121
125
|
};
|
|
122
|
-
if (foreignKeysSymbol) {
|
|
123
|
-
// @ts-ignore
|
|
124
|
-
const foreignKeys: any[] = table[foreignKeysSymbol];
|
|
125
|
-
if (foreignKeys) {
|
|
126
|
-
for (const foreignKey of foreignKeys) {
|
|
127
|
-
builders.foreignKeys.push(foreignKey);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
126
|
|
|
132
127
|
// Process extra configuration if available
|
|
133
128
|
if (extraSymbol) {
|
|
134
129
|
// @ts-ignore
|
|
135
130
|
const extraConfigBuilder = table[extraSymbol];
|
|
136
131
|
if (extraConfigBuilder && typeof extraConfigBuilder === "function") {
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
132
|
+
const configBuilderData = extraConfigBuilder(table);
|
|
133
|
+
if (configBuilderData) {
|
|
134
|
+
// Convert configBuilderData to array if it's an object
|
|
135
|
+
const configBuilders = Array.isArray(configBuilderData)
|
|
136
|
+
? configBuilderData
|
|
137
|
+
: Object.values(configBuilderData).map(
|
|
138
|
+
(item) => (item as ConfigBuilderData).value || item,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
// Process each builder
|
|
142
|
+
configBuilders.forEach((builder) => {
|
|
143
|
+
if (!builder?.constructor) return;
|
|
144
|
+
|
|
145
|
+
const builderName = builder.constructor.name.toLowerCase();
|
|
146
|
+
|
|
147
|
+
// Map builder types to their corresponding arrays
|
|
148
|
+
const builderMap = {
|
|
149
|
+
indexbuilder: builders.indexes,
|
|
150
|
+
checkbuilder: builders.checks,
|
|
151
|
+
foreignkeybuilder: builders.foreignKeys,
|
|
152
|
+
primarykeybuilder: builders.primaryKeys,
|
|
153
|
+
uniqueconstraintbuilder: builders.uniqueConstraints,
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Add builder to appropriate array if it matches any type
|
|
157
|
+
for (const [type, array] of Object.entries(builderMap)) {
|
|
158
|
+
if (builderName.includes(type)) {
|
|
159
|
+
array.push(builder);
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Always add to extras array
|
|
165
|
+
builders.extras.push(builder);
|
|
166
|
+
});
|
|
143
167
|
}
|
|
144
|
-
configBuildersArray.forEach((builder) => {
|
|
145
|
-
if (builder instanceof IndexBuilder) {
|
|
146
|
-
builders.indexes.push(builder);
|
|
147
|
-
} else if (builder instanceof CheckBuilder) {
|
|
148
|
-
builders.checks.push(builder);
|
|
149
|
-
} else if (builder instanceof ForeignKeyBuilder) {
|
|
150
|
-
builders.foreignKeys.push(builder);
|
|
151
|
-
} else if (builder instanceof PrimaryKeyBuilder) {
|
|
152
|
-
builders.primaryKeys.push(builder);
|
|
153
|
-
} else if (builder instanceof UniqueConstraintBuilder) {
|
|
154
|
-
builders.uniqueConstraints.push(builder);
|
|
155
|
-
}
|
|
156
|
-
builders.extras.push(builder);
|
|
157
|
-
});
|
|
158
168
|
}
|
|
159
169
|
}
|
|
160
170
|
|