forge-sql-orm 2.0.0 → 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.
@@ -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 this.executeQuery<T>(query);
37
+ const results: Awaited<T> = await query;
169
38
  const datas = results as unknown[];
170
39
  if (!datas.length) {
171
40
  return undefined;
package/src/index.ts CHANGED
@@ -4,5 +4,6 @@ export * from "./core/ForgeSQLQueryBuilder";
4
4
  export * from "./core/ForgeSQLCrudOperations";
5
5
  export * from "./core/ForgeSQLSelectOperations";
6
6
  export * from "./utils/sqlUtils";
7
+ export * from "./utils/forgeDriver";
7
8
 
8
9
  export default ForgeSQLORM;
@@ -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;