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.
Files changed (41) hide show
  1. package/README.md +67 -42
  2. package/dist/ForgeSQLORM.js +162 -16
  3. package/dist/ForgeSQLORM.js.map +1 -1
  4. package/dist/ForgeSQLORM.mjs +165 -19
  5. package/dist/ForgeSQLORM.mjs.map +1 -1
  6. package/dist/core/ForgeSQLCrudOperations.d.ts.map +1 -1
  7. package/dist/core/ForgeSQLORM.d.ts.map +1 -1
  8. package/dist/core/ForgeSQLQueryBuilder.d.ts +32 -0
  9. package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
  10. package/dist/core/SystemTables.d.ts +8 -6
  11. package/dist/core/SystemTables.d.ts.map +1 -1
  12. package/dist/index.d.ts +1 -0
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/lib/drizzle/extensions/selectAliased.d.ts +9 -0
  15. package/dist/lib/drizzle/extensions/selectAliased.d.ts.map +1 -0
  16. package/dist/utils/sqlUtils.d.ts +8 -4
  17. package/dist/utils/sqlUtils.d.ts.map +1 -1
  18. package/dist/webtriggers/applyMigrationsWebTrigger.d.ts +23 -0
  19. package/dist/webtriggers/applyMigrationsWebTrigger.d.ts.map +1 -1
  20. package/dist/webtriggers/dropMigrationWebTrigger.d.ts +20 -4
  21. package/dist/webtriggers/dropMigrationWebTrigger.d.ts.map +1 -1
  22. package/dist/webtriggers/fetchSchemaWebTrigger.d.ts +28 -0
  23. package/dist/webtriggers/fetchSchemaWebTrigger.d.ts.map +1 -0
  24. package/dist/webtriggers/index.d.ts +1 -0
  25. package/dist/webtriggers/index.d.ts.map +1 -1
  26. package/dist-cli/cli.js +5 -0
  27. package/dist-cli/cli.js.map +1 -1
  28. package/dist-cli/cli.mjs +8 -3
  29. package/dist-cli/cli.mjs.map +1 -1
  30. package/package.json +10 -10
  31. package/src/core/ForgeSQLCrudOperations.ts +0 -425
  32. package/src/core/ForgeSQLORM.ts +0 -228
  33. package/src/core/ForgeSQLQueryBuilder.ts +0 -319
  34. package/src/core/ForgeSQLSelectOperations.ts +0 -93
  35. package/src/core/SystemTables.ts +0 -7
  36. package/src/index.ts +0 -10
  37. package/src/utils/forgeDriver.ts +0 -39
  38. package/src/utils/sqlUtils.ts +0 -306
  39. package/src/webtriggers/applyMigrationsWebTrigger.ts +0 -26
  40. package/src/webtriggers/dropMigrationWebTrigger.ts +0 -35
  41. package/src/webtriggers/index.ts +0 -25
@@ -1,10 +1,10 @@
1
- import { sql, isTable, eq, and } from "drizzle-orm";
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 fieldAlias = sql.raw(`${tableName}_${column.name}`);
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
- let aliasName = sql.raw(`${getTableName(column.table)}_${column.name}`);
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.select(mapSelectFieldsWithAlias(fields));
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.selectDistinct(mapSelectFieldsWithAlias(fields));
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.getDrizzleQueryBuilder().select(mapSelectFieldsWithAlias(fields));
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.getDrizzleQueryBuilder().selectDistinct(mapSelectFieldsWithAlias(fields));
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 migrations = await migration(migrationRunner);
796
- const successfulMigrations = await migrations.run();
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
- mapSelectTableToAlias,
840
- parseDateTime
985
+ parseDateTime,
986
+ patchDbWithSelectAliased
841
987
  };
842
988
  //# sourceMappingURL=ForgeSQLORM.mjs.map