forge-sql-orm 2.0.17 → 2.0.19

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 +95 -4
  2. package/dist/ForgeSQLORM.js +382 -60
  3. package/dist/ForgeSQLORM.js.map +1 -1
  4. package/dist/ForgeSQLORM.mjs +382 -60
  5. package/dist/ForgeSQLORM.mjs.map +1 -1
  6. package/dist/core/ForgeSQLAnalyseOperations.d.ts +250 -0
  7. package/dist/core/ForgeSQLAnalyseOperations.d.ts.map +1 -0
  8. package/dist/core/ForgeSQLCrudOperations.d.ts +1 -1
  9. package/dist/core/ForgeSQLCrudOperations.d.ts.map +1 -1
  10. package/dist/core/ForgeSQLORM.d.ts +12 -2
  11. package/dist/core/ForgeSQLORM.d.ts.map +1 -1
  12. package/dist/core/ForgeSQLQueryBuilder.d.ts +112 -21
  13. package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
  14. package/dist/core/ForgeSQLSelectOperations.d.ts.map +1 -1
  15. package/dist/core/SystemTables.d.ts +167 -0
  16. package/dist/core/SystemTables.d.ts.map +1 -1
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/utils/forgeDriverProxy.d.ts +11 -0
  20. package/dist/utils/forgeDriverProxy.d.ts.map +1 -0
  21. package/dist/utils/sqlHints.d.ts +21 -0
  22. package/dist/utils/sqlHints.d.ts.map +1 -0
  23. package/dist/utils/sqlUtils.d.ts +2 -8
  24. package/dist/utils/sqlUtils.d.ts.map +1 -1
  25. package/dist/webtriggers/applyMigrationsWebTrigger.d.ts.map +1 -1
  26. package/dist/webtriggers/dropMigrationWebTrigger.d.ts +2 -4
  27. package/dist/webtriggers/dropMigrationWebTrigger.d.ts.map +1 -1
  28. package/package.json +4 -12
  29. package/src/core/ForgeSQLAnalyseOperations.ts +461 -0
  30. package/src/core/ForgeSQLCrudOperations.ts +15 -8
  31. package/src/core/ForgeSQLORM.ts +46 -9
  32. package/src/core/ForgeSQLQueryBuilder.ts +129 -32
  33. package/src/core/ForgeSQLSelectOperations.ts +4 -6
  34. package/src/core/SystemTables.ts +175 -0
  35. package/src/index.ts +1 -0
  36. package/src/utils/forgeDriverProxy.ts +27 -0
  37. package/src/utils/sqlHints.ts +63 -0
  38. package/src/utils/sqlUtils.ts +36 -32
  39. package/src/webtriggers/applyMigrationsWebTrigger.ts +32 -16
  40. package/src/webtriggers/dropMigrationWebTrigger.ts +5 -6
  41. package/src/webtriggers/fetchSchemaWebTrigger.ts +2 -10
@@ -47,23 +47,24 @@ interface ConfigBuilderData {
47
47
  * @returns Date object
48
48
  */
49
49
  export const parseDateTime = (value: string, format: string): Date => {
50
+ let result: Date;
50
51
  const m = moment(value, format, true);
51
52
  if (!m.isValid()) {
52
- return moment(value).toDate();
53
+ const momentDate = moment(value);
54
+ if (momentDate.isValid()) {
55
+ result = momentDate.toDate();
56
+ } else {
57
+ result = new Date(value);
58
+ }
59
+ } else {
60
+ result = m.toDate();
61
+ }
62
+ if (isNaN(result.getTime())) {
63
+ result = new Date(value);
53
64
  }
54
- return m.toDate();
65
+ return result;
55
66
  };
56
67
 
57
- /**
58
- * Extracts the alias from a SQL query
59
- * @param query - The SQL query to extract the alias from
60
- * @returns The extracted alias or the original query if no alias found
61
- */
62
- export function extractAlias(query: string): string {
63
- const match = query.match(/\bas\s+(['"`]?)([\w*]+)\1$/i);
64
- return match ? match[2] : query;
65
- }
66
-
67
68
  /**
68
69
  * Gets primary keys from the schema.
69
70
  * @template T - The type of the table schema
@@ -144,7 +145,7 @@ function processForeignKeys(
144
145
  const configBuilders = Array.isArray(configBuilderData)
145
146
  ? configBuilderData
146
147
  : Object.values(configBuilderData).map(
147
- (item) => (item as ConfigBuilderData).value || item,
148
+ (item) => (item as ConfigBuilderData).value ?? item,
148
149
  );
149
150
 
150
151
  configBuilders.forEach((builder) => {
@@ -198,7 +199,7 @@ export function getTableMetadata(table: AnyMySqlTable): MetadataInfo {
198
199
  const configBuilders = Array.isArray(configBuilderData)
199
200
  ? configBuilderData
200
201
  : Object.values(configBuilderData).map(
201
- (item) => (item as ConfigBuilderData).value || item,
202
+ (item) => (item as ConfigBuilderData).value ?? item,
202
203
  );
203
204
 
204
205
  // Process each builder
@@ -239,28 +240,26 @@ export function getTableMetadata(table: AnyMySqlTable): MetadataInfo {
239
240
 
240
241
  /**
241
242
  * Generates SQL statements to drop tables
242
- * @param tables - Array of table schemas
243
+ * @param tables - Array of table names
243
244
  * @returns Array of SQL statements for dropping tables
244
245
  */
245
- export function generateDropTableStatements(tables: AnyMySqlTable[]): string[] {
246
+ export function generateDropTableStatements(tables: string[]): string[] {
246
247
  const dropStatements: string[] = [];
247
248
 
248
- tables.forEach((table) => {
249
- const tableMetadata = getTableMetadata(table);
250
- if (tableMetadata.tableName) {
251
- dropStatements.push(`DROP TABLE IF EXISTS \`${tableMetadata.tableName}\`;`);
252
- }
249
+ tables.forEach((tableName) => {
250
+ dropStatements.push(`DROP TABLE IF EXISTS \`${tableName}\`;`);
253
251
  });
254
252
 
255
- // Add statement to clear migrations table
256
- dropStatements.push(`DELETE FROM __migrations;`);
257
-
258
253
  return dropStatements;
259
254
  }
260
255
 
261
256
  type AliasColumnMap = Record<string, AnyColumn>;
262
257
 
263
- function mapSelectTableToAlias(table: MySqlTable,uniqPrefix:string, aliasMap: AliasColumnMap): any {
258
+ function mapSelectTableToAlias(
259
+ table: MySqlTable,
260
+ uniqPrefix: string,
261
+ aliasMap: AliasColumnMap,
262
+ ): any {
264
263
  const { columns, tableName } = getTableMetadata(table);
265
264
  const selectionsTableFields: Record<string, unknown> = {};
266
265
  Object.keys(columns).forEach((name) => {
@@ -280,7 +279,7 @@ function isDrizzleColumn(column: any): boolean {
280
279
  export function mapSelectAllFieldsToAlias(
281
280
  selections: any,
282
281
  name: string,
283
- uniqName:string,
282
+ uniqName: string,
284
283
  fields: any,
285
284
  aliasMap: AliasColumnMap,
286
285
  ): any {
@@ -312,7 +311,7 @@ export function mapSelectFieldsWithAlias<TSelection extends SelectedFields>(
312
311
  const aliasMap: AliasColumnMap = {};
313
312
  const selections: any = {};
314
313
  Object.entries(fields).forEach(([name, fields]) => {
315
- mapSelectAllFieldsToAlias(selections, name,name, fields, aliasMap);
314
+ mapSelectAllFieldsToAlias(selections, name, name, fields, aliasMap);
316
315
  });
317
316
  return { selections, aliasMap };
318
317
  }
@@ -327,9 +326,9 @@ function getAliasFromDrizzleAlias(value: unknown): string | undefined {
327
326
  const aliasNameChunk = queryChunks[queryChunks.length - 2];
328
327
  if (isSQLWrapper(aliasNameChunk) && "queryChunks" in aliasNameChunk) {
329
328
  const aliasNameChunkSql = aliasNameChunk as SQL;
330
- if (aliasNameChunkSql && aliasNameChunkSql.queryChunks.length === 1) {
329
+ if (aliasNameChunkSql.queryChunks?.length === 1 && aliasNameChunkSql.queryChunks[0]) {
331
330
  const queryChunksStringChunc = aliasNameChunkSql.queryChunks[0];
332
- if (queryChunksStringChunc && "value" in queryChunksStringChunc) {
331
+ if ("value" in queryChunksStringChunc) {
333
332
  const values = (queryChunksStringChunc as StringChunk).value;
334
333
  if (values && values.length === 1) {
335
334
  return values[0];
@@ -402,7 +401,12 @@ export function applyFromDriverTransform<T, TSelection>(
402
401
  }
403
402
 
404
403
  function processNullBranches(obj: Record<string, unknown>): Record<string, unknown> | null {
405
- if (obj === null || typeof obj !== 'object' || obj===undefined) {
404
+ if (obj === null || typeof obj !== "object") {
405
+ return obj;
406
+ }
407
+
408
+ // Skip built-in objects like Date, Array, etc.
409
+ if (obj.constructor && obj.constructor.name !== "Object") {
406
410
  return obj;
407
411
  }
408
412
 
@@ -410,12 +414,12 @@ function processNullBranches(obj: Record<string, unknown>): Record<string, unkno
410
414
  let allNull = true;
411
415
 
412
416
  for (const [key, value] of Object.entries(obj)) {
413
- if (value === null || value===undefined) {
417
+ if (value === null || value === undefined) {
414
418
  result[key] = null;
415
419
  continue;
416
420
  }
417
421
 
418
- if (typeof value === 'object' && (value !== null || value !== undefined) ) {
422
+ if (typeof value === "object") {
419
423
  const processed = processNullBranches(value as Record<string, unknown>);
420
424
  result[key] = processed;
421
425
  if (processed !== null) {
@@ -27,23 +27,39 @@ import { MigrationRunner } from "@forge/sql/out/migration";
27
27
  export const applySchemaMigrations = async (
28
28
  migration: (migrationRunner: MigrationRunner) => Promise<MigrationRunner>,
29
29
  ) => {
30
- console.log("Provisioning the database");
31
- await sql._provision();
32
- console.info("Running schema migrations");
33
- const migrations = await migration(migrationRunner);
34
- const successfulMigrations = await migrations.run();
35
- console.info("Migrations applied:", successfulMigrations);
30
+ try {
31
+ if (typeof migration !== "function") {
32
+ throw new Error("migration is not a function");
33
+ }
36
34
 
37
- const migrationHistory = (await migrationRunner.list())
38
- .map((y) => `${y.id}, ${y.name}, ${y.migratedAt.toUTCString()}`)
39
- .join("\n");
35
+ console.log("Provisioning the database");
36
+ await sql._provision();
37
+ console.info("Running schema migrations");
38
+ const migrations = await migration(migrationRunner);
39
+ const successfulMigrations = await migrations.run();
40
+ console.info("Migrations applied:", successfulMigrations);
40
41
 
41
- console.info("Migrations history:\nid, name, migrated_at\n", migrationHistory);
42
+ const migrationList = await migrationRunner.list();
43
+ const migrationHistory =
44
+ Array.isArray(migrationList) && migrationList.length > 0
45
+ ? migrationList.map((y) => `${y.id}, ${y.name}, ${y.migratedAt.toUTCString()}`).join("\n")
46
+ : "No migrations found";
42
47
 
43
- return {
44
- headers: { "Content-Type": ["application/json"] },
45
- statusCode: 200,
46
- statusText: "OK",
47
- body: "Migrations successfully executed",
48
- };
48
+ console.info("Migrations history:\nid, name, migrated_at\n", migrationHistory);
49
+
50
+ return {
51
+ headers: { "Content-Type": ["application/json"] },
52
+ statusCode: 200,
53
+ statusText: "OK",
54
+ body: "Migrations successfully executed",
55
+ };
56
+ } catch (error) {
57
+ console.error("Error during migration:", error);
58
+ return {
59
+ headers: { "Content-Type": ["application/json"] },
60
+ statusCode: 500,
61
+ statusText: "Internal Server Error",
62
+ body: error instanceof Error ? error.message : "Unknown error during migration",
63
+ };
64
+ }
49
65
  };
@@ -1,7 +1,7 @@
1
1
  import { sql } from "@forge/sql";
2
- import { AnyMySqlTable } from "drizzle-orm/mysql-core";
3
2
  import { generateDropTableStatements as generateStatements } from "../utils/sqlUtils";
4
3
  import { getHttpResponse, TriggerResponse } from "./index";
4
+ import { getTables } from "../core/SystemTables";
5
5
 
6
6
  /**
7
7
  * ⚠️ DEVELOPMENT ONLY WEB TRIGGER ⚠️
@@ -15,7 +15,6 @@ import { getHttpResponse, TriggerResponse } from "./index";
15
15
  * - It may affect application functionality
16
16
  * - It could lead to data loss and system instability
17
17
  *
18
- * @param tables - Array of table schemas to drop
19
18
  * @returns {Promise<TriggerResponse<string>>} A response containing:
20
19
  * - On success: 200 status with warning message about permanent deletion
21
20
  * - On failure: 500 status with error message
@@ -23,14 +22,13 @@ import { getHttpResponse, TriggerResponse } from "./index";
23
22
  * @example
24
23
  * ```typescript
25
24
  * // Example usage in development only
26
- * await dropSchemaMigrations([users, orders]);
25
+ * await dropSchemaMigrations();
27
26
  * // ⚠️ Warning: This will permanently delete all data in users and orders tables
28
27
  * ```
29
28
  */
30
- export async function dropSchemaMigrations(
31
- tables: AnyMySqlTable[],
32
- ): Promise<TriggerResponse<string>> {
29
+ export async function dropSchemaMigrations(): Promise<TriggerResponse<string>> {
33
30
  try {
31
+ const tables = await getTables();
34
32
  // Generate drop statements
35
33
  const dropStatements = generateStatements(tables);
36
34
 
@@ -45,6 +43,7 @@ export async function dropSchemaMigrations(
45
43
  "⚠️ All data in these tables has been permanently deleted. This operation cannot be undone.",
46
44
  );
47
45
  } catch (error: unknown) {
46
+ console.error(error);
48
47
  const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
49
48
  return getHttpResponse<string>(500, errorMessage);
50
49
  }
@@ -1,6 +1,6 @@
1
1
  import { sql } from "@forge/sql";
2
2
  import { getHttpResponse, TriggerResponse } from "./index";
3
- import { forgeSystemTables } from "../core/SystemTables";
3
+ import { forgeSystemTables, getTables } from "../core/SystemTables";
4
4
  import { getTableName } from "drizzle-orm/table";
5
5
 
6
6
  interface CreateTableRow {
@@ -47,14 +47,6 @@ export async function fetchSchemaWebTrigger(): Promise<TriggerResponse<string>>
47
47
  }
48
48
  }
49
49
 
50
- /**
51
- * Retrieves all tables from the database
52
- */
53
- async function getTables(): Promise<string[]> {
54
- const tables = await sql.executeDDL<string>("SHOW TABLES");
55
- return tables.rows.flatMap((tableInfo) => Object.values(tableInfo));
56
- }
57
-
58
50
  /**
59
51
  * Generates CREATE TABLE statements for each table
60
52
  */
@@ -62,7 +54,7 @@ async function generateCreateTableStatements(tables: string[]): Promise<string[]
62
54
  const statements: string[] = [];
63
55
 
64
56
  for (const table of tables) {
65
- const createTableResult = await sql.executeDDL<CreateTableRow>(`SHOW CREATE TABLE ${table}`);
57
+ const createTableResult = await sql.executeDDL<CreateTableRow>(`SHOW CREATE TABLE "${table}"`);
66
58
 
67
59
  const createTableStatements = createTableResult.rows
68
60
  .filter((row) => !isSystemTable(row.Table))