forge-sql-orm 2.0.9 → 2.0.11
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 +67 -42
- package/dist/ForgeSQLORM.js +162 -16
- package/dist/ForgeSQLORM.js.map +1 -1
- package/dist/ForgeSQLORM.mjs +165 -19
- package/dist/ForgeSQLORM.mjs.map +1 -1
- package/dist/core/ForgeSQLCrudOperations.d.ts.map +1 -1
- package/dist/core/ForgeSQLORM.d.ts.map +1 -1
- package/dist/core/ForgeSQLQueryBuilder.d.ts +32 -0
- package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
- package/dist/core/SystemTables.d.ts +8 -6
- package/dist/core/SystemTables.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/drizzle/extensions/selectAliased.d.ts +9 -0
- package/dist/lib/drizzle/extensions/selectAliased.d.ts.map +1 -0
- package/dist/utils/sqlUtils.d.ts +8 -4
- package/dist/utils/sqlUtils.d.ts.map +1 -1
- package/dist/webtriggers/applyMigrationsWebTrigger.d.ts +23 -0
- package/dist/webtriggers/applyMigrationsWebTrigger.d.ts.map +1 -1
- package/dist/webtriggers/dropMigrationWebTrigger.d.ts +20 -4
- package/dist/webtriggers/dropMigrationWebTrigger.d.ts.map +1 -1
- package/dist/webtriggers/fetchSchemaWebTrigger.d.ts +28 -0
- package/dist/webtriggers/fetchSchemaWebTrigger.d.ts.map +1 -0
- package/dist/webtriggers/index.d.ts +1 -0
- package/dist/webtriggers/index.d.ts.map +1 -1
- package/dist-cli/cli.js +5 -0
- package/dist-cli/cli.js.map +1 -1
- package/dist-cli/cli.mjs +8 -3
- package/dist-cli/cli.mjs.map +1 -1
- package/package.json +10 -10
- package/src/core/ForgeSQLCrudOperations.ts +0 -425
- package/src/core/ForgeSQLORM.ts +0 -228
- package/src/core/ForgeSQLQueryBuilder.ts +0 -319
- package/src/core/ForgeSQLSelectOperations.ts +0 -93
- package/src/core/SystemTables.ts +0 -7
- package/src/index.ts +0 -10
- package/src/utils/forgeDriver.ts +0 -39
- package/src/utils/sqlUtils.ts +0 -306
- package/src/webtriggers/applyMigrationsWebTrigger.ts +0 -26
- package/src/webtriggers/dropMigrationWebTrigger.ts +0 -35
- package/src/webtriggers/index.ts +0 -25
package/dist/ForgeSQLORM.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isTable, sql, eq, and } from "drizzle-orm";
|
|
2
2
|
import moment from "moment";
|
|
3
3
|
import { getTableName } from "drizzle-orm/table";
|
|
4
4
|
import { isSQLWrapper } from "drizzle-orm/sql/sql";
|
|
5
5
|
import { sql as sql$1, migrationRunner } from "@forge/sql";
|
|
6
6
|
import { drizzle } from "drizzle-orm/mysql-proxy";
|
|
7
|
-
import { customType } from "drizzle-orm/mysql-core";
|
|
7
|
+
import { customType, mysqlTable, timestamp, varchar, bigint } from "drizzle-orm/mysql-core";
|
|
8
8
|
import moment$1 from "moment/moment.js";
|
|
9
9
|
const parseDateTime = (value, format) => {
|
|
10
10
|
const m = moment(value, format, true);
|
|
@@ -129,32 +129,36 @@ function generateDropTableStatements(tables) {
|
|
|
129
129
|
dropStatements.push(`DELETE FROM __migrations;`);
|
|
130
130
|
return dropStatements;
|
|
131
131
|
}
|
|
132
|
-
function mapSelectTableToAlias(table) {
|
|
132
|
+
function mapSelectTableToAlias(table, aliasMap) {
|
|
133
133
|
const { columns, tableName } = getTableMetadata(table);
|
|
134
134
|
const selectionsTableFields = {};
|
|
135
135
|
Object.keys(columns).forEach((name) => {
|
|
136
136
|
const column = columns[name];
|
|
137
|
-
const
|
|
137
|
+
const uniqName = `a_${tableName}_${column.name}`;
|
|
138
|
+
const fieldAlias = sql.raw(uniqName);
|
|
138
139
|
selectionsTableFields[name] = sql`${column} as \`${fieldAlias}\``;
|
|
140
|
+
aliasMap[uniqName] = column;
|
|
139
141
|
});
|
|
140
142
|
return selectionsTableFields;
|
|
141
143
|
}
|
|
142
144
|
function isDrizzleColumn(column) {
|
|
143
145
|
return column && typeof column === "object" && "table" in column;
|
|
144
146
|
}
|
|
145
|
-
function mapSelectAllFieldsToAlias(selections, name, fields) {
|
|
147
|
+
function mapSelectAllFieldsToAlias(selections, name, fields, aliasMap) {
|
|
146
148
|
if (isTable(fields)) {
|
|
147
|
-
selections[name] = mapSelectTableToAlias(fields);
|
|
149
|
+
selections[name] = mapSelectTableToAlias(fields, aliasMap);
|
|
148
150
|
} else if (isDrizzleColumn(fields)) {
|
|
149
151
|
const column = fields;
|
|
150
|
-
|
|
152
|
+
const uniqName = `a_${getTableName(column.table)}_${column.name}`;
|
|
153
|
+
let aliasName = sql.raw(uniqName);
|
|
151
154
|
selections[name] = sql`${column} as \`${aliasName}\``;
|
|
155
|
+
aliasMap[uniqName] = column;
|
|
152
156
|
} else if (isSQLWrapper(fields)) {
|
|
153
157
|
selections[name] = fields;
|
|
154
158
|
} else {
|
|
155
159
|
const innerSelections = {};
|
|
156
160
|
Object.entries(fields).forEach(([iname, ifields]) => {
|
|
157
|
-
mapSelectAllFieldsToAlias(innerSelections, iname, ifields);
|
|
161
|
+
mapSelectAllFieldsToAlias(innerSelections, iname, ifields, aliasMap);
|
|
158
162
|
});
|
|
159
163
|
selections[name] = innerSelections;
|
|
160
164
|
}
|
|
@@ -164,11 +168,65 @@ function mapSelectFieldsWithAlias(fields) {
|
|
|
164
168
|
if (!fields) {
|
|
165
169
|
throw new Error("fields is empty");
|
|
166
170
|
}
|
|
171
|
+
const aliasMap = {};
|
|
167
172
|
const selections = {};
|
|
168
173
|
Object.entries(fields).forEach(([name, fields2]) => {
|
|
169
|
-
mapSelectAllFieldsToAlias(selections, name, fields2);
|
|
174
|
+
mapSelectAllFieldsToAlias(selections, name, fields2, aliasMap);
|
|
175
|
+
});
|
|
176
|
+
return { selections, aliasMap };
|
|
177
|
+
}
|
|
178
|
+
function getAliasFromDrizzleAlias(value) {
|
|
179
|
+
const isSQL = value !== null && typeof value === "object" && isSQLWrapper(value) && "queryChunks" in value;
|
|
180
|
+
if (isSQL) {
|
|
181
|
+
const sql2 = value;
|
|
182
|
+
const queryChunks = sql2.queryChunks;
|
|
183
|
+
if (queryChunks.length > 3) {
|
|
184
|
+
const aliasNameChunk = queryChunks[queryChunks.length - 2];
|
|
185
|
+
if (isSQLWrapper(aliasNameChunk) && "queryChunks" in aliasNameChunk) {
|
|
186
|
+
const aliasNameChunkSql = aliasNameChunk;
|
|
187
|
+
if (aliasNameChunkSql && aliasNameChunkSql.queryChunks.length === 1) {
|
|
188
|
+
const queryChunksStringChunc = aliasNameChunkSql.queryChunks[0];
|
|
189
|
+
if (queryChunksStringChunc && "value" in queryChunksStringChunc) {
|
|
190
|
+
const values = queryChunksStringChunc.value;
|
|
191
|
+
if (values && values.length === 1) {
|
|
192
|
+
return values[0];
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return void 0;
|
|
200
|
+
}
|
|
201
|
+
function transformValue(value, alias, aliasMap) {
|
|
202
|
+
const column = aliasMap[alias];
|
|
203
|
+
if (!column) return value;
|
|
204
|
+
let customColumn = column;
|
|
205
|
+
const fromDriver = customColumn?.mapFrom;
|
|
206
|
+
if (fromDriver && value !== null && value !== void 0) {
|
|
207
|
+
return fromDriver(value);
|
|
208
|
+
}
|
|
209
|
+
return value;
|
|
210
|
+
}
|
|
211
|
+
function transformObject(obj, selections, aliasMap) {
|
|
212
|
+
const result = {};
|
|
213
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
214
|
+
const selection = selections[key];
|
|
215
|
+
const alias = getAliasFromDrizzleAlias(selection);
|
|
216
|
+
if (alias && aliasMap[alias]) {
|
|
217
|
+
result[key] = transformValue(value, alias, aliasMap);
|
|
218
|
+
} else if (selection && typeof selection === "object" && !isSQLWrapper(selection)) {
|
|
219
|
+
result[key] = transformObject(value, selection, aliasMap);
|
|
220
|
+
} else {
|
|
221
|
+
result[key] = value;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
function applyFromDriverTransform(rows, selections, aliasMap) {
|
|
227
|
+
return rows.map((row) => {
|
|
228
|
+
return transformObject(row, selections, aliasMap);
|
|
170
229
|
});
|
|
171
|
-
return selections;
|
|
172
230
|
}
|
|
173
231
|
class ForgeSQLCrudOperations {
|
|
174
232
|
forgeOperations;
|
|
@@ -547,6 +605,52 @@ const forgeDriver = async (query, params, method) => {
|
|
|
547
605
|
throw error;
|
|
548
606
|
}
|
|
549
607
|
};
|
|
608
|
+
function createAliasedSelectBuilder(db, fields, selectFn) {
|
|
609
|
+
const { selections, aliasMap } = mapSelectFieldsWithAlias(fields);
|
|
610
|
+
const builder = selectFn(selections);
|
|
611
|
+
const wrapBuilder = (rawBuilder) => {
|
|
612
|
+
return new Proxy(rawBuilder, {
|
|
613
|
+
get(target, prop, receiver) {
|
|
614
|
+
if (prop === "execute") {
|
|
615
|
+
return async (...args) => {
|
|
616
|
+
const rows = await target.execute(...args);
|
|
617
|
+
return applyFromDriverTransform(rows, selections, aliasMap);
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
if (prop === "then") {
|
|
621
|
+
return (onfulfilled, onrejected) => target.execute().then(
|
|
622
|
+
(rows) => {
|
|
623
|
+
const transformed = applyFromDriverTransform(rows, selections, aliasMap);
|
|
624
|
+
return onfulfilled?.(transformed);
|
|
625
|
+
},
|
|
626
|
+
onrejected
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
const value = Reflect.get(target, prop, receiver);
|
|
630
|
+
if (typeof value === "function") {
|
|
631
|
+
return (...args) => {
|
|
632
|
+
const result = value.apply(target, args);
|
|
633
|
+
if (typeof result === "object" && result !== null && "execute" in result) {
|
|
634
|
+
return wrapBuilder(result);
|
|
635
|
+
}
|
|
636
|
+
return result;
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
return value;
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
};
|
|
643
|
+
return wrapBuilder(builder);
|
|
644
|
+
}
|
|
645
|
+
function patchDbWithSelectAliased(db) {
|
|
646
|
+
db.selectAliased = function(fields) {
|
|
647
|
+
return createAliasedSelectBuilder(db, fields, (selections) => db.select(selections));
|
|
648
|
+
};
|
|
649
|
+
db.selectAliasedDistinct = function(fields) {
|
|
650
|
+
return createAliasedSelectBuilder(db, fields, (selections) => db.selectDistinct(selections));
|
|
651
|
+
};
|
|
652
|
+
return db;
|
|
653
|
+
}
|
|
550
654
|
class ForgeSQLORMImpl {
|
|
551
655
|
static instance = null;
|
|
552
656
|
drizzle;
|
|
@@ -565,7 +669,7 @@ class ForgeSQLORMImpl {
|
|
|
565
669
|
if (newOptions.logRawSqlQuery) {
|
|
566
670
|
console.debug("Initializing ForgeSQLORM...");
|
|
567
671
|
}
|
|
568
|
-
this.drizzle = drizzle(forgeDriver, { logger: newOptions.logRawSqlQuery });
|
|
672
|
+
this.drizzle = patchDbWithSelectAliased(drizzle(forgeDriver, { logger: newOptions.logRawSqlQuery }));
|
|
569
673
|
this.crudOperations = new ForgeSQLCrudOperations(this, newOptions);
|
|
570
674
|
this.fetchOperations = new ForgeSQLSelectOperations(newOptions);
|
|
571
675
|
} catch (error) {
|
|
@@ -630,7 +734,7 @@ class ForgeSQLORMImpl {
|
|
|
630
734
|
if (!fields) {
|
|
631
735
|
throw new Error("fields is empty");
|
|
632
736
|
}
|
|
633
|
-
return this.drizzle.
|
|
737
|
+
return this.drizzle.selectAliased(fields);
|
|
634
738
|
}
|
|
635
739
|
/**
|
|
636
740
|
* Creates a distinct select query with unique field aliases to prevent field name collisions in joins.
|
|
@@ -652,7 +756,7 @@ class ForgeSQLORMImpl {
|
|
|
652
756
|
if (!fields) {
|
|
653
757
|
throw new Error("fields is empty");
|
|
654
758
|
}
|
|
655
|
-
return this.drizzle.
|
|
759
|
+
return this.drizzle.selectAliasedDistinct(fields);
|
|
656
760
|
}
|
|
657
761
|
}
|
|
658
762
|
class ForgeSQLORM {
|
|
@@ -677,7 +781,7 @@ class ForgeSQLORM {
|
|
|
677
781
|
* ```
|
|
678
782
|
*/
|
|
679
783
|
select(fields) {
|
|
680
|
-
return this.ormInstance.
|
|
784
|
+
return this.ormInstance.select(fields);
|
|
681
785
|
}
|
|
682
786
|
/**
|
|
683
787
|
* Creates a distinct select query with unique field aliases to prevent field name collisions in joins.
|
|
@@ -696,7 +800,7 @@ class ForgeSQLORM {
|
|
|
696
800
|
* ```
|
|
697
801
|
*/
|
|
698
802
|
selectDistinct(fields) {
|
|
699
|
-
return this.ormInstance.
|
|
803
|
+
return this.ormInstance.selectDistinct(fields);
|
|
700
804
|
}
|
|
701
805
|
/**
|
|
702
806
|
* Proxies the `crud` method from `ForgeSQLORMImpl`.
|
|
@@ -792,8 +896,8 @@ const applySchemaMigrations = async (migration) => {
|
|
|
792
896
|
console.log("Provisioning the database");
|
|
793
897
|
await sql$1._provision();
|
|
794
898
|
console.info("Running schema migrations");
|
|
795
|
-
const
|
|
796
|
-
const successfulMigrations = await
|
|
899
|
+
const migrations2 = await migration(migrationRunner);
|
|
900
|
+
const successfulMigrations = await migrations2.run();
|
|
797
901
|
console.info("Migrations applied:", successfulMigrations);
|
|
798
902
|
const migrationHistory = (await migrationRunner.list()).map((y) => `${y.id}, ${y.name}, ${y.migratedAt.toUTCString()}`).join("\n");
|
|
799
903
|
console.info("Migrations history:\nid, name, migrated_at\n", migrationHistory);
|
|
@@ -804,6 +908,46 @@ const applySchemaMigrations = async (migration) => {
|
|
|
804
908
|
body: "Migrations successfully executed"
|
|
805
909
|
};
|
|
806
910
|
};
|
|
911
|
+
const migrations = mysqlTable("__migrations", {
|
|
912
|
+
id: bigint("id", { mode: "number" }).primaryKey().autoincrement(),
|
|
913
|
+
name: varchar("name", { length: 255 }).notNull(),
|
|
914
|
+
migratedAt: timestamp("migratedAt").defaultNow().notNull()
|
|
915
|
+
});
|
|
916
|
+
const forgeSystemTables = [migrations];
|
|
917
|
+
async function fetchSchemaWebTrigger() {
|
|
918
|
+
try {
|
|
919
|
+
const tables = await getTables();
|
|
920
|
+
const createTableStatements = await generateCreateTableStatements(tables);
|
|
921
|
+
const sqlStatements = wrapWithForeignKeyChecks(createTableStatements);
|
|
922
|
+
return getHttpResponse(200, sqlStatements.join(";\n"));
|
|
923
|
+
} catch (error) {
|
|
924
|
+
console.error(JSON.stringify(error));
|
|
925
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
926
|
+
return getHttpResponse(500, errorMessage);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
async function getTables() {
|
|
930
|
+
const tables = await sql$1.executeDDL("SHOW TABLES");
|
|
931
|
+
return tables.rows.flatMap((tableInfo) => Object.values(tableInfo));
|
|
932
|
+
}
|
|
933
|
+
async function generateCreateTableStatements(tables) {
|
|
934
|
+
const statements = [];
|
|
935
|
+
for (const table of tables) {
|
|
936
|
+
const createTableResult = await sql$1.executeDDL(`SHOW CREATE TABLE ${table}`);
|
|
937
|
+
const createTableStatements = createTableResult.rows.filter((row) => !isSystemTable(row.Table)).map((row) => formatCreateTableStatement(row["Create Table"]));
|
|
938
|
+
statements.push(...createTableStatements);
|
|
939
|
+
}
|
|
940
|
+
return statements;
|
|
941
|
+
}
|
|
942
|
+
function isSystemTable(tableName) {
|
|
943
|
+
return forgeSystemTables.some((st) => getTableName(st) === tableName);
|
|
944
|
+
}
|
|
945
|
+
function formatCreateTableStatement(statement) {
|
|
946
|
+
return statement.replace(/"/g, "").replace("CREATE TABLE", "CREATE TABLE IF NOT EXISTS");
|
|
947
|
+
}
|
|
948
|
+
function wrapWithForeignKeyChecks(statements) {
|
|
949
|
+
return ["SET foreign_key_checks = 0", ...statements, "SET foreign_key_checks = 1"];
|
|
950
|
+
}
|
|
807
951
|
const getHttpResponse = (statusCode, body) => {
|
|
808
952
|
let statusText = "";
|
|
809
953
|
if (statusCode === 200) {
|
|
@@ -821,10 +965,12 @@ const getHttpResponse = (statusCode, body) => {
|
|
|
821
965
|
export {
|
|
822
966
|
ForgeSQLCrudOperations,
|
|
823
967
|
ForgeSQLSelectOperations,
|
|
968
|
+
applyFromDriverTransform,
|
|
824
969
|
applySchemaMigrations,
|
|
825
970
|
ForgeSQLORM as default,
|
|
826
971
|
dropSchemaMigrations,
|
|
827
972
|
extractAlias,
|
|
973
|
+
fetchSchemaWebTrigger,
|
|
828
974
|
forgeDateString,
|
|
829
975
|
forgeDateTimeString,
|
|
830
976
|
forgeDriver,
|
|
@@ -836,7 +982,7 @@ export {
|
|
|
836
982
|
getTableMetadata,
|
|
837
983
|
mapSelectAllFieldsToAlias,
|
|
838
984
|
mapSelectFieldsWithAlias,
|
|
839
|
-
|
|
840
|
-
|
|
985
|
+
parseDateTime,
|
|
986
|
+
patchDbWithSelectAliased
|
|
841
987
|
};
|
|
842
988
|
//# sourceMappingURL=ForgeSQLORM.mjs.map
|