forge-sql-orm 2.0.18 → 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 (33) hide show
  1. package/README.md +95 -4
  2. package/dist/ForgeSQLORM.js +314 -49
  3. package/dist/ForgeSQLORM.js.map +1 -1
  4. package/dist/ForgeSQLORM.mjs +314 -49
  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/ForgeSQLORM.d.ts +12 -2
  9. package/dist/core/ForgeSQLORM.d.ts.map +1 -1
  10. package/dist/core/ForgeSQLQueryBuilder.d.ts +105 -9
  11. package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
  12. package/dist/core/ForgeSQLSelectOperations.d.ts.map +1 -1
  13. package/dist/core/SystemTables.d.ts +167 -0
  14. package/dist/core/SystemTables.d.ts.map +1 -1
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/utils/sqlUtils.d.ts +2 -2
  18. package/dist/utils/sqlUtils.d.ts.map +1 -1
  19. package/dist/webtriggers/applyMigrationsWebTrigger.d.ts.map +1 -1
  20. package/dist/webtriggers/dropMigrationWebTrigger.d.ts +2 -4
  21. package/dist/webtriggers/dropMigrationWebTrigger.d.ts.map +1 -1
  22. package/package.json +4 -12
  23. package/src/core/ForgeSQLAnalyseOperations.ts +461 -0
  24. package/src/core/ForgeSQLORM.ts +43 -7
  25. package/src/core/ForgeSQLQueryBuilder.ts +121 -18
  26. package/src/core/ForgeSQLSelectOperations.ts +4 -6
  27. package/src/core/SystemTables.ts +175 -0
  28. package/src/index.ts +1 -0
  29. package/src/utils/forgeDriverProxy.ts +1 -1
  30. package/src/utils/sqlUtils.ts +10 -16
  31. package/src/webtriggers/applyMigrationsWebTrigger.ts +32 -16
  32. package/src/webtriggers/dropMigrationWebTrigger.ts +5 -6
  33. package/src/webtriggers/fetchSchemaWebTrigger.ts +2 -10
@@ -10,40 +10,61 @@ import {
10
10
  MySqlSelectDynamic,
11
11
  type SelectedFields,
12
12
  } from "drizzle-orm/mysql-core/query-builders/select.types";
13
- import { InferInsertModel, SQL } from "drizzle-orm";
13
+ import { InferInsertModel, Query, SQL } from "drizzle-orm";
14
14
  import moment from "moment/moment";
15
15
  import { parseDateTime } from "../utils/sqlUtils";
16
16
  import { MySqlRemoteDatabase, MySqlRemotePreparedQueryHKT } from "drizzle-orm/mysql-proxy/index";
17
17
  import { SqlHints } from "../utils/sqlHints";
18
-
19
- // ============= Core Types =============
18
+ import {
19
+ ClusterStatementRowCamelCase,
20
+ ExplainAnalyzeRow,
21
+ SlowQueryNormalized,
22
+ } from "./SystemTables";
20
23
 
21
24
  /**
22
- * Interface representing the main ForgeSQL operations.
23
- * Provides access to CRUD operations and schema-level SQL operations.
25
+ * Core interface for ForgeSQL operations.
26
+ * Provides access to CRUD operations, schema-level SQL operations, and query analysis capabilities.
27
+ *
28
+ * @interface ForgeSqlOperation
29
+ * @extends {QueryBuilderForgeSql}
24
30
  */
25
31
  export interface ForgeSqlOperation extends QueryBuilderForgeSql {
26
32
  /**
27
- * Provides CRUD (Create, Read, Update, Delete) operations.
33
+ * Provides CRUD (Create, Update, Delete) operations.
34
+ * @deprecated Use modify() instead for better type safety and consistency
28
35
  * @returns {CRUDForgeSQL} Interface for performing CRUD operations
29
36
  */
30
37
  crud(): CRUDForgeSQL;
31
38
 
32
39
  /**
33
- * Provides schema-level SQL fetch operations.
40
+ * Provides modify (Create, Update, Delete) operations with optimistic locking support.
41
+ * @returns {CRUDForgeSQL} Interface for performing CRUD operations
42
+ */
43
+ modify(): CRUDForgeSQL;
44
+
45
+ /**
46
+ * Provides schema-level SQL fetch operations with type safety.
34
47
  * @returns {SchemaSqlForgeSql} Interface for executing schema-bound SQL queries
35
48
  */
36
49
  fetch(): SchemaSqlForgeSql;
50
+
51
+ /**
52
+ * Provides query analysis capabilities including EXPLAIN ANALYZE and slow query analysis.
53
+ * @returns {SchemaAnalyzeForgeSql} Interface for analyzing query performance
54
+ */
55
+ analyze(): SchemaAnalyzeForgeSql;
37
56
  }
38
57
 
39
58
  /**
40
59
  * Interface for Query Builder operations.
41
- * Provides access to the underlying Drizzle ORM query builder.
60
+ * Provides access to the underlying Drizzle ORM query builder with enhanced functionality.
61
+ *
62
+ * @interface QueryBuilderForgeSql
42
63
  */
43
64
  export interface QueryBuilderForgeSql {
44
65
  /**
45
66
  * Creates a new query builder for the given entity.
46
- * @returns {MySql2Database<Record<string, unknown>>} The Drizzle database instance for building queries
67
+ * @returns {MySqlRemoteDatabase<Record<string, unknown>>} The Drizzle database instance for building queries
47
68
  */
48
69
  getDrizzleQueryBuilder(): MySqlRemoteDatabase<Record<string, unknown>>;
49
70
 
@@ -88,18 +109,18 @@ export interface QueryBuilderForgeSql {
88
109
  ): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
89
110
  }
90
111
 
91
- // ============= CRUD Operations =============
92
-
93
112
  /**
94
- * Interface for CRUD (Create, Read, Update, Delete) operations.
113
+ * Interface for Modify (Create, Update, Delete) operations.
95
114
  * Provides methods for basic database operations with support for optimistic locking.
115
+ *
116
+ * @interface CRUDForgeSQL
96
117
  */
97
118
  export interface CRUDForgeSQL {
98
119
  /**
99
120
  * Inserts multiple records into the database.
100
121
  * @template T - The type of the table schema
101
122
  * @param {T} schema - The entity schema
102
- * @param {<InferInsertModel<T>[]} models - The list of entities to insert
123
+ * @param {InferInsertModel<T>[]} models - The list of entities to insert
103
124
  * @param {boolean} [updateIfExists] - Whether to update the row if it already exists (default: false)
104
125
  * @returns {Promise<number>} The number of inserted rows
105
126
  * @throws {Error} If the insert operation fails
@@ -159,11 +180,81 @@ export interface CRUDForgeSQL {
159
180
  ): Promise<number>;
160
181
  }
161
182
 
162
- // ============= Schema SQL Operations =============
183
+ /**
184
+ * Interface for schema analysis operations.
185
+ * Provides methods for analyzing query performance and execution plans.
186
+ *
187
+ * @interface SchemaAnalyzeForgeSql
188
+ */
189
+ export interface SchemaAnalyzeForgeSql {
190
+ /**
191
+ * Executes EXPLAIN on a Drizzle query.
192
+ * @param {{ toSQL: () => Query }} query - The Drizzle query to analyze
193
+ * @returns {Promise<ExplainAnalyzeRow[]>} The execution plan analysis results
194
+ */
195
+ explain(query: { toSQL: () => Query }): Promise<ExplainAnalyzeRow[]>;
196
+
197
+ /**
198
+ * Executes EXPLAIN on a raw SQL query.
199
+ * @param {string} query - The SQL query to analyze
200
+ * @param {unknown[]} bindParams - The query parameters
201
+ * @returns {Promise<ExplainAnalyzeRow[]>} The execution plan analysis results
202
+ */
203
+ explainRaw(query: string, bindParams: unknown[]): Promise<ExplainAnalyzeRow[]>;
204
+
205
+ /**
206
+ * Executes EXPLAIN ANALYZE on a Drizzle query.
207
+ * @param {{ toSQL: () => Query }} query - The Drizzle query to analyze
208
+ * @returns {Promise<ExplainAnalyzeRow[]>} The execution plan analysis results
209
+ */
210
+ explainAnalyze(query: { toSQL: () => Query }): Promise<ExplainAnalyzeRow[]>;
211
+
212
+ /**
213
+ * Executes EXPLAIN ANALYZE on a raw SQL query.
214
+ * @param {string} query - The SQL query to analyze
215
+ * @param {unknown[]} bindParams - The query parameters
216
+ * @returns {Promise<ExplainAnalyzeRow[]>} The execution plan analysis results
217
+ */
218
+ explainAnalyzeRaw(query: string, bindParams: unknown[]): Promise<ExplainAnalyzeRow[]>;
219
+
220
+ /**
221
+ * Analyzes slow queries from the database.
222
+ * @returns {Promise<SlowQueryNormalized[]>} The normalized slow query data
223
+ */
224
+ analyzeSlowQueries(): Promise<SlowQueryNormalized[]>;
225
+
226
+ /**
227
+ * Analyzes query history for specific tables using Drizzle table objects.
228
+ * @param {AnyMySqlTable[]} tables - The Drizzle table objects to analyze
229
+ * @param {Date} [fromDate] - The start date for the analysis
230
+ * @param {Date} [toDate] - The end date for the analysis
231
+ * @returns {Promise<ClusterStatementRowCamelCase[]>} The analyzed query history
232
+ */
233
+ analyzeQueriesHistory(
234
+ tables: AnyMySqlTable[],
235
+ fromDate?: Date,
236
+ toDate?: Date,
237
+ ): Promise<ClusterStatementRowCamelCase[]>;
238
+
239
+ /**
240
+ * Analyzes query history for specific tables using raw table names.
241
+ * @param {string[]} tables - The table names to analyze
242
+ * @param {Date} [fromDate] - The start date for the analysis
243
+ * @param {Date} [toDate] - The end date for the analysis
244
+ * @returns {Promise<ClusterStatementRowCamelCase[]>} The analyzed query history
245
+ */
246
+ analyzeQueriesHistoryRaw(
247
+ tables: string[],
248
+ fromDate?: Date,
249
+ toDate?: Date,
250
+ ): Promise<ClusterStatementRowCamelCase[]>;
251
+ }
163
252
 
164
253
  /**
165
254
  * Interface for schema-level SQL operations.
166
255
  * Provides methods for executing SQL queries with schema binding and type safety.
256
+ *
257
+ * @interface SchemaSqlForgeSql
167
258
  */
168
259
  export interface SchemaSqlForgeSql {
169
260
  /**
@@ -200,11 +291,11 @@ export interface SchemaSqlForgeSql {
200
291
  executeRawUpdateSQL(query: string, params?: unknown[]): Promise<UpdateQueryResponse>;
201
292
  }
202
293
 
203
- // ============= Configuration Types =============
204
-
205
294
  /**
206
295
  * Interface for version field metadata.
207
296
  * Defines the configuration for optimistic locking version fields.
297
+ *
298
+ * @interface VersionFieldMetadata
208
299
  */
209
300
  export interface VersionFieldMetadata {
210
301
  /** Name of the version field */
@@ -214,6 +305,8 @@ export interface VersionFieldMetadata {
214
305
  /**
215
306
  * Interface for table metadata.
216
307
  * Defines the configuration for a specific table.
308
+ *
309
+ * @interface TableMetadata
217
310
  */
218
311
  export interface TableMetadata {
219
312
  /** Name of the table */
@@ -225,11 +318,15 @@ export interface TableMetadata {
225
318
  /**
226
319
  * Type for additional metadata configuration.
227
320
  * Maps table names to their metadata configuration.
321
+ *
322
+ * @type {AdditionalMetadata}
228
323
  */
229
324
  export type AdditionalMetadata = Record<string, TableMetadata>;
230
325
 
231
326
  /**
232
327
  * Interface for ForgeSQL ORM options
328
+ *
329
+ * @interface ForgeSqlOrmOptions
233
330
  */
234
331
  export interface ForgeSqlOrmOptions {
235
332
  /** Whether to log raw SQL queries */
@@ -259,11 +356,11 @@ export interface ForgeSqlOrmOptions {
259
356
  additionalMetadata?: AdditionalMetadata;
260
357
  }
261
358
 
262
- // ============= Custom Types =============
263
-
264
359
  /**
265
360
  * Custom type for MySQL datetime fields.
266
361
  * Handles conversion between JavaScript Date objects and MySQL datetime strings.
362
+ *
363
+ * @type {CustomType}
267
364
  */
268
365
  export const forgeDateTimeString = customType<{
269
366
  data: Date;
@@ -285,6 +382,8 @@ export const forgeDateTimeString = customType<{
285
382
  /**
286
383
  * Custom type for MySQL timestamp fields.
287
384
  * Handles conversion between JavaScript Date objects and MySQL timestamp strings.
385
+ *
386
+ * @type {CustomType}
288
387
  */
289
388
  export const forgeTimestampString = customType<{
290
389
  data: Date;
@@ -306,6 +405,8 @@ export const forgeTimestampString = customType<{
306
405
  /**
307
406
  * Custom type for MySQL date fields.
308
407
  * Handles conversion between JavaScript Date objects and MySQL date strings.
408
+ *
409
+ * @type {CustomType}
309
410
  */
310
411
  export const forgeDateString = customType<{
311
412
  data: Date;
@@ -327,6 +428,8 @@ export const forgeDateString = customType<{
327
428
  /**
328
429
  * Custom type for MySQL time fields.
329
430
  * Handles conversion between JavaScript Date objects and MySQL time strings.
431
+ *
432
+ * @type {CustomType}
330
433
  */
331
434
  export const forgeTimeString = customType<{
332
435
  data: Date;
@@ -57,9 +57,8 @@ export class ForgeSQLSelectOperations implements SchemaSqlForgeSql {
57
57
  */
58
58
  async executeRawSQL<T extends object | unknown>(query: string, params?: unknown[]): Promise<T[]> {
59
59
  if (this.options.logRawSqlQuery) {
60
- console.debug(
61
- `Executing with SQL ${query}` + params ? `, with params: ${JSON.stringify(params)}` : "",
62
- );
60
+ const paramsStr = params ? `, with params: ${JSON.stringify(params)}` : "";
61
+ console.debug(`Executing with SQL ${query}${paramsStr}`);
63
62
  }
64
63
  const sqlStatement = sql.prepare<T>(query);
65
64
  if (params) {
@@ -82,9 +81,8 @@ export class ForgeSQLSelectOperations implements SchemaSqlForgeSql {
82
81
  }
83
82
  if (this.options.logRawSqlQuery) {
84
83
  console.debug(
85
- `Executing Update with SQL ${query}` + params
86
- ? `, with params: ${JSON.stringify(params)}`
87
- : "",
84
+ `Executing Update with SQL ${query}` +
85
+ (params ? `, with params: ${JSON.stringify(params)}` : ""),
88
86
  );
89
87
  }
90
88
  const updateQueryResponseResults = await sqlStatement.execute();
@@ -1,5 +1,6 @@
1
1
  import { bigint, mysqlTable, timestamp, varchar } from "drizzle-orm/mysql-core";
2
2
  import { Table } from "drizzle-orm";
3
+ import { sql } from "@forge/sql";
3
4
 
4
5
  export const migrations = mysqlTable("__migrations", {
5
6
  id: bigint("id", { mode: "number" }).primaryKey().autoincrement(),
@@ -7,4 +8,178 @@ export const migrations = mysqlTable("__migrations", {
7
8
  migratedAt: timestamp("migratedAt").defaultNow().notNull(),
8
9
  });
9
10
 
11
+ export interface ExplainAnalyzeRow {
12
+ id: string;
13
+ estRows?: string;
14
+ estCost?: string;
15
+ actRows?: string;
16
+ task?: string;
17
+ accessObject?: string;
18
+ executionInfo?: string;
19
+ operatorInfo?: string;
20
+ memory?: string;
21
+ disk?: string;
22
+ }
23
+
24
+ export interface SlowQueryNormalized {
25
+ time: string;
26
+ txnStartTs: number;
27
+ user: string;
28
+ host: string;
29
+ connId: number;
30
+ db: string;
31
+ query: string;
32
+ digest: string;
33
+ queryTime: number;
34
+ compileTime: number;
35
+ optimizeTime: number;
36
+ processTime: number;
37
+ waitTime: number;
38
+ parseTime: number;
39
+ rewriteTime: number;
40
+ copTime: number;
41
+ copProcAvg: number;
42
+ copProcMax: number;
43
+ copProcP90: number;
44
+ copProcAddr: string;
45
+ copWaitAvg: number;
46
+ copWaitMax: number;
47
+ copWaitP90: number;
48
+ copWaitAddr: string;
49
+ memMax: number;
50
+ diskMax: number;
51
+ totalKeys: number;
52
+ processKeys: number;
53
+ requestCount: number;
54
+ kvTotal: number;
55
+ pdTotal: number;
56
+ resultRows: number;
57
+ rocksdbBlockCacheHitCount: number;
58
+ rocksdbBlockReadCount: number;
59
+ rocksdbBlockReadByte: number;
60
+ plan: string;
61
+ parsedPlan?: ExplainAnalyzeRow[];
62
+ binaryPlan: string;
63
+ planDigest: string;
64
+ }
65
+
66
+ export interface ClusterStatementRowCamelCase {
67
+ instance: string;
68
+ summaryBeginTime: string;
69
+ summaryEndTime: string;
70
+ stmtType: string;
71
+ schemaName: string;
72
+ digest: string;
73
+ digestText: string;
74
+ tableNames: string;
75
+ indexNames: string | null;
76
+ sampleUser: string;
77
+ execCount: number;
78
+ sumErrors: number;
79
+ sumWarnings: number;
80
+ sumLatency: number;
81
+ maxLatency: number;
82
+ minLatency: number;
83
+ avgLatency: number;
84
+ avgParseLatency: number;
85
+ maxParseLatency: number;
86
+ avgCompileLatency: number;
87
+ maxCompileLatency: number;
88
+ sumCopTaskNum: number;
89
+ maxCopProcessTime: number;
90
+ maxCopProcessAddress: string;
91
+ maxCopWaitTime: number;
92
+ maxCopWaitAddress: string;
93
+ avgProcessTime: number;
94
+ maxProcessTime: number;
95
+ avgWaitTime: number;
96
+ maxWaitTime: number;
97
+ avgBackoffTime: number;
98
+ maxBackoffTime: number;
99
+ avgTotalKeys: number;
100
+ maxTotalKeys: number;
101
+ avgProcessedKeys: number;
102
+ maxProcessedKeys: number;
103
+ avgRocksdbDeleteSkippedCount: number;
104
+ maxRocksdbDeleteSkippedCount: number;
105
+ avgRocksdbKeySkippedCount: number;
106
+ maxRocksdbKeySkippedCount: number;
107
+ avgRocksdbBlockCacheHitCount: number;
108
+ maxRocksdbBlockCacheHitCount: number;
109
+ avgRocksdbBlockReadCount: number;
110
+ maxRocksdbBlockReadCount: number;
111
+ avgRocksdbBlockReadByte: number;
112
+ maxRocksdbBlockReadByte: number;
113
+ avgPrewriteTime: number;
114
+ maxPrewriteTime: number;
115
+ avgCommitTime: number;
116
+ maxCommitTime: number;
117
+ avgGetCommitTsTime: number;
118
+ maxGetCommitTsTime: number;
119
+ avgCommitBackoffTime: number;
120
+ maxCommitBackoffTime: number;
121
+ avgResolveLockTime: number;
122
+ maxResolveLockTime: number;
123
+ avgLocalLatchWaitTime: number;
124
+ maxLocalLatchWaitTime: number;
125
+ avgWriteKeys: number;
126
+ maxWriteKeys: number;
127
+ avgWriteSize: number;
128
+ maxWriteSize: number;
129
+ avgPrewriteRegions: number;
130
+ maxPrewriteRegions: number;
131
+ avgTxnRetry: number;
132
+ maxTxnRetry: number;
133
+ sumExecRetry: number;
134
+ sumExecRetryTime: number;
135
+ sumBackoffTimes: number;
136
+ backoffTypes: string | null;
137
+ avgMem: number;
138
+ maxMem: number;
139
+ avgDisk: number;
140
+ maxDisk: number;
141
+ avgKvTime: number;
142
+ avgPdTime: number;
143
+ avgBackoffTotalTime: number;
144
+ avgWriteSqlRespTime: number;
145
+ avgTidbCpuTime: number;
146
+ avgTikvCpuTime: number;
147
+ maxResultRows: number;
148
+ minResultRows: number;
149
+ avgResultRows: number;
150
+ prepared: number;
151
+ avgAffectedRows: number;
152
+ firstSeen: string;
153
+ lastSeen: string;
154
+ planInCache: number;
155
+ planCacheHits: number;
156
+ planInBinding: number;
157
+ querySampleText: string;
158
+ prevSampleText: string;
159
+ planDigest: string;
160
+ plan: string;
161
+ binaryPlan: string;
162
+ charset: string;
163
+ collation: string;
164
+ planHint: string;
165
+ maxRequestUnitRead: number;
166
+ avgRequestUnitRead: number;
167
+ maxRequestUnitWrite: number;
168
+ avgRequestUnitWrite: number;
169
+ maxQueuedRcTime: number;
170
+ avgQueuedRcTime: number;
171
+ resourceGroup: string;
172
+ planCacheUnqualified: number;
173
+ planCacheUnqualifiedLastReason: string;
174
+ parsedPlan?: ExplainAnalyzeRow[];
175
+ }
176
+
177
+ /**
178
+ * Retrieves all tables from the database
179
+ */
180
+ export async function getTables(): Promise<string[]> {
181
+ const tables = await sql.executeDDL<string>("SHOW TABLES");
182
+ return tables.rows.flatMap((tableInfo) => Object.values(tableInfo));
183
+ }
184
+
10
185
  export const forgeSystemTables: Table[] = [migrations];
package/src/index.ts CHANGED
@@ -7,5 +7,6 @@ export * from "./utils/sqlUtils";
7
7
  export * from "./utils/forgeDriver";
8
8
  export * from "./webtriggers";
9
9
  export * from "./lib/drizzle/extensions/selectAliased";
10
+ export * from "./core/SystemTables";
10
11
 
11
12
  export default ForgeSQLORM;
@@ -18,7 +18,7 @@ export function createForgeDriverProxy(options?: SqlHints, logRawSqlQuery?: bool
18
18
  // Inject SQL hints into the query
19
19
  const modifiedQuery = injectSqlHints(query, options);
20
20
 
21
- if (options && logRawSqlQuery) {
21
+ if (options && logRawSqlQuery && modifiedQuery !== query) {
22
22
  console.warn("modified query: " + modifiedQuery);
23
23
  }
24
24
  // Call the original forgeDriver with the modified query
@@ -145,7 +145,7 @@ function processForeignKeys(
145
145
  const configBuilders = Array.isArray(configBuilderData)
146
146
  ? configBuilderData
147
147
  : Object.values(configBuilderData).map(
148
- (item) => (item as ConfigBuilderData).value || item,
148
+ (item) => (item as ConfigBuilderData).value ?? item,
149
149
  );
150
150
 
151
151
  configBuilders.forEach((builder) => {
@@ -199,7 +199,7 @@ export function getTableMetadata(table: AnyMySqlTable): MetadataInfo {
199
199
  const configBuilders = Array.isArray(configBuilderData)
200
200
  ? configBuilderData
201
201
  : Object.values(configBuilderData).map(
202
- (item) => (item as ConfigBuilderData).value || item,
202
+ (item) => (item as ConfigBuilderData).value ?? item,
203
203
  );
204
204
 
205
205
  // Process each builder
@@ -240,22 +240,16 @@ export function getTableMetadata(table: AnyMySqlTable): MetadataInfo {
240
240
 
241
241
  /**
242
242
  * Generates SQL statements to drop tables
243
- * @param tables - Array of table schemas
243
+ * @param tables - Array of table names
244
244
  * @returns Array of SQL statements for dropping tables
245
245
  */
246
- export function generateDropTableStatements(tables: AnyMySqlTable[]): string[] {
246
+ export function generateDropTableStatements(tables: string[]): string[] {
247
247
  const dropStatements: string[] = [];
248
248
 
249
- tables.forEach((table) => {
250
- const tableMetadata = getTableMetadata(table);
251
- if (tableMetadata.tableName) {
252
- dropStatements.push(`DROP TABLE IF EXISTS \`${tableMetadata.tableName}\`;`);
253
- }
249
+ tables.forEach((tableName) => {
250
+ dropStatements.push(`DROP TABLE IF EXISTS \`${tableName}\`;`);
254
251
  });
255
252
 
256
- // Add statement to clear migrations table
257
- dropStatements.push(`DELETE FROM __migrations;`);
258
-
259
253
  return dropStatements;
260
254
  }
261
255
 
@@ -332,9 +326,9 @@ function getAliasFromDrizzleAlias(value: unknown): string | undefined {
332
326
  const aliasNameChunk = queryChunks[queryChunks.length - 2];
333
327
  if (isSQLWrapper(aliasNameChunk) && "queryChunks" in aliasNameChunk) {
334
328
  const aliasNameChunkSql = aliasNameChunk as SQL;
335
- if (aliasNameChunkSql && aliasNameChunkSql.queryChunks.length === 1) {
329
+ if (aliasNameChunkSql.queryChunks?.length === 1 && aliasNameChunkSql.queryChunks[0]) {
336
330
  const queryChunksStringChunc = aliasNameChunkSql.queryChunks[0];
337
- if (queryChunksStringChunc && "value" in queryChunksStringChunc) {
331
+ if ("value" in queryChunksStringChunc) {
338
332
  const values = (queryChunksStringChunc as StringChunk).value;
339
333
  if (values && values.length === 1) {
340
334
  return values[0];
@@ -407,7 +401,7 @@ export function applyFromDriverTransform<T, TSelection>(
407
401
  }
408
402
 
409
403
  function processNullBranches(obj: Record<string, unknown>): Record<string, unknown> | null {
410
- if (obj === null || typeof obj !== "object" || obj === undefined) {
404
+ if (obj === null || typeof obj !== "object") {
411
405
  return obj;
412
406
  }
413
407
 
@@ -425,7 +419,7 @@ function processNullBranches(obj: Record<string, unknown>): Record<string, unkno
425
419
  continue;
426
420
  }
427
421
 
428
- if (typeof value === "object" && value !== null && value !== undefined) {
422
+ if (typeof value === "object") {
429
423
  const processed = processNullBranches(value as Record<string, unknown>);
430
424
  result[key] = processed;
431
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))