forge-sql-orm 2.1.18 → 2.1.21

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 (60) hide show
  1. package/dist/async/index.d.ts +2 -0
  2. package/dist/async/index.d.ts.map +1 -0
  3. package/dist/async/index.js +18 -0
  4. package/dist/async/index.js.map +1 -0
  5. package/dist/core/ForgeSQLQueryBuilder.d.ts +55 -2
  6. package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
  7. package/dist/core/ForgeSQLQueryBuilder.js.map +1 -1
  8. package/dist/core/Rovo.d.ts +104 -50
  9. package/dist/core/Rovo.d.ts.map +1 -1
  10. package/dist/core/Rovo.js +174 -59
  11. package/dist/core/Rovo.js.map +1 -1
  12. package/dist/core/index.d.ts +5 -0
  13. package/dist/core/index.d.ts.map +1 -0
  14. package/dist/core/index.js +21 -0
  15. package/dist/core/index.js.map +1 -0
  16. package/dist/index.d.ts +4 -8
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +4 -8
  19. package/dist/index.js.map +1 -1
  20. package/dist/lib/index.d.ts +2 -0
  21. package/dist/lib/index.d.ts.map +1 -0
  22. package/dist/lib/index.js +18 -0
  23. package/dist/lib/index.js.map +1 -0
  24. package/dist/utils/index.d.ts +3 -0
  25. package/dist/utils/index.d.ts.map +1 -0
  26. package/dist/utils/index.js +19 -0
  27. package/dist/utils/index.js.map +1 -0
  28. package/dist/utils/sqlUtils.d.ts +9 -0
  29. package/dist/utils/sqlUtils.d.ts.map +1 -1
  30. package/dist/utils/sqlUtils.js +21 -0
  31. package/dist/utils/sqlUtils.js.map +1 -1
  32. package/dist/webtriggers/clearCacheSchedulerTrigger.d.ts +2 -0
  33. package/dist/webtriggers/clearCacheSchedulerTrigger.d.ts.map +1 -1
  34. package/dist/webtriggers/clearCacheSchedulerTrigger.js +2 -0
  35. package/dist/webtriggers/clearCacheSchedulerTrigger.js.map +1 -1
  36. package/dist/webtriggers/dropMigrationWebTrigger.d.ts +3 -1
  37. package/dist/webtriggers/dropMigrationWebTrigger.d.ts.map +1 -1
  38. package/dist/webtriggers/dropMigrationWebTrigger.js +7 -1
  39. package/dist/webtriggers/dropMigrationWebTrigger.js.map +1 -1
  40. package/dist/webtriggers/dropTablesMigrationWebTrigger.d.ts +3 -1
  41. package/dist/webtriggers/dropTablesMigrationWebTrigger.d.ts.map +1 -1
  42. package/dist/webtriggers/dropTablesMigrationWebTrigger.js +7 -1
  43. package/dist/webtriggers/dropTablesMigrationWebTrigger.js.map +1 -1
  44. package/dist/webtriggers/fetchSchemaWebTrigger.d.ts +3 -1
  45. package/dist/webtriggers/fetchSchemaWebTrigger.d.ts.map +1 -1
  46. package/dist/webtriggers/fetchSchemaWebTrigger.js +8 -1
  47. package/dist/webtriggers/fetchSchemaWebTrigger.js.map +1 -1
  48. package/package.json +5 -4
  49. package/src/async/index.ts +1 -0
  50. package/src/core/ForgeSQLQueryBuilder.ts +59 -2
  51. package/src/core/Rovo.ts +181 -59
  52. package/src/core/index.ts +4 -0
  53. package/src/index.ts +8 -8
  54. package/src/lib/index.ts +1 -0
  55. package/src/utils/index.ts +2 -0
  56. package/src/utils/sqlUtils.ts +21 -0
  57. package/src/webtriggers/clearCacheSchedulerTrigger.ts +2 -0
  58. package/src/webtriggers/dropMigrationWebTrigger.ts +11 -2
  59. package/src/webtriggers/dropTablesMigrationWebTrigger.ts +11 -2
  60. package/src/webtriggers/fetchSchemaWebTrigger.ts +8 -1
package/src/core/Rovo.ts CHANGED
@@ -31,10 +31,10 @@ class RovoIntegrationSettingImpl implements RovoIntegrationSetting {
31
31
  *
32
32
  * @param {string} accountId - The account ID of the active user
33
33
  * @param {string} tableName - The name of the table to query
34
- * @param {Record<string, string>} contextParam - Context parameters for query substitution
34
+ * @param {Record<string, string>} contextParam - Context parameters for query substitution (parameter name -> value mapping)
35
35
  * @param {boolean} rls - Whether Row-Level Security is enabled
36
36
  * @param {string[]} rlsFields - Array of field names required for RLS validation
37
- * @param {(alias: string) => string} rlsWherePart - Function that generates WHERE clause for RLS
37
+ * @param {(alias: string) => string} rlsWherePart - Function that generates WHERE clause for RLS filtering
38
38
  */
39
39
  constructor(
40
40
  accountId: string,
@@ -135,21 +135,83 @@ class RovoIntegrationSettingCreatorImpl implements RovoIntegrationSettingCreator
135
135
  this.accountId = accountId;
136
136
  }
137
137
 
138
+ /**
139
+ * Adds a string context parameter for query substitution.
140
+ * The value will be wrapped in single quotes in the SQL query.
141
+ *
142
+ * @param {string} parameterName - The parameter name to replace in the query (e.g., '{{projectKey}}')
143
+ * @param {string} value - The string value to substitute for the parameter
144
+ * @returns {RovoIntegrationSettingCreator} This builder instance for method chaining
145
+ *
146
+ * @example
147
+ * ```typescript
148
+ * builder.addStringContextParameter('{{projectKey}}', 'PROJ-123');
149
+ * // In SQL: {{projectKey}} will be replaced with 'PROJ-123'
150
+ * ```
151
+ */
152
+ addStringContextParameter(parameterName: string, value: string): RovoIntegrationSettingCreator {
153
+ this.addContextParameter(parameterName, value, true);
154
+ return this;
155
+ }
156
+
157
+ /**
158
+ * Adds a number context parameter for query substitution.
159
+ * The value will be inserted as-is without quotes in the SQL query.
160
+ *
161
+ * @param {string} parameterName - The parameter name to replace in the query (e.g., '{{limit}}')
162
+ * @param {number} value - The numeric value to substitute for the parameter
163
+ * @returns {RovoIntegrationSettingCreator} This builder instance for method chaining
164
+ *
165
+ * @example
166
+ * ```typescript
167
+ * builder.addNumberContextParameter('{{limit}}', 100);
168
+ * // In SQL: {{limit}} will be replaced with 100
169
+ * ```
170
+ */
171
+ addNumberContextParameter(parameterName: string, value: number): RovoIntegrationSettingCreator {
172
+ this.addContextParameter(parameterName, String(value), false);
173
+ return this;
174
+ }
175
+
176
+ /**
177
+ * Adds a boolean context parameter for query substitution.
178
+ * The value will be converted to 1 (true) or 0 (false) and inserted as a number.
179
+ *
180
+ * @param {string} parameterName - The parameter name to replace in the query (e.g., '{{isActive}}')
181
+ * @param {boolean} value - The boolean value to substitute for the parameter
182
+ * @returns {RovoIntegrationSettingCreator} This builder instance for method chaining
183
+ *
184
+ * @example
185
+ * ```typescript
186
+ * builder.addBooleanContextParameter('{{isActive}}', true);
187
+ * // In SQL: {{isActive}} will be replaced with 1
188
+ * ```
189
+ */
190
+ addBooleanContextParameter(parameterName: string, value: boolean): RovoIntegrationSettingCreator {
191
+ this.addNumberContextParameter(parameterName, value ? 1 : 0);
192
+ return this;
193
+ }
138
194
  /**
139
195
  * Adds a context parameter for query substitution.
140
196
  * Context parameters are replaced in the SQL query before execution.
141
197
  *
142
- * @param {string} parameterName - The parameter name to replace in the query
198
+ * @param {string} parameterName - The parameter name to replace in the query (e.g., '{{projectKey}}')
143
199
  * @param {string} value - The value to substitute for the parameter
200
+ * @param {boolean} wrap - Whether to wrap the value in single quotes (true for strings, false for numbers)
144
201
  * @returns {RovoIntegrationSettingCreator} This builder instance for method chaining
145
202
  *
146
203
  * @example
147
204
  * ```typescript
148
- * builder.addContextParameter('{{projectKey}}', 'PROJ-123');
205
+ * builder.addContextParameter('{{projectKey}}', 'PROJ-123', true);
206
+ * // In SQL: {{projectKey}} will be replaced with 'PROJ-123'
149
207
  * ```
150
208
  */
151
- addContextParameter(parameterName: string, value: string): RovoIntegrationSettingCreator {
152
- this.contextParam[parameterName] = value;
209
+ addContextParameter(
210
+ parameterName: string,
211
+ value: string,
212
+ wrap: boolean,
213
+ ): RovoIntegrationSettingCreator {
214
+ this.contextParam[parameterName] = wrap ? `'${value}'` : value;
153
215
  return this;
154
216
  }
155
217
 
@@ -179,6 +241,12 @@ class RovoIntegrationSettingCreatorImpl implements RovoIntegrationSettingCreator
179
241
  private isUseRlsConditionalSettings: () => Promise<boolean> = async () => true;
180
242
  private rlsFieldsSettings: string[] = [];
181
243
  private wherePartSettings: (alias: string) => string = () => "";
244
+
245
+ /**
246
+ * Creates a new RlsSettingsImpl instance.
247
+ *
248
+ * @param {RovoIntegrationSettingCreatorImpl} parent - The parent settings builder instance
249
+ */
182
250
  constructor(private readonly parent: RovoIntegrationSettingCreatorImpl) {}
183
251
  /**
184
252
  * Sets a conditional function to determine if RLS should be applied.
@@ -328,8 +396,8 @@ export class Rovo implements RovoIntegration {
328
396
  /**
329
397
  * Creates a new Rovo instance.
330
398
  *
331
- * @param {ForgeSqlOperation} forgeSqlOperations - The ForgeSQL operations instance for query analysis
332
- * @param options - Configuration options for the ORM
399
+ * @param {ForgeSqlOperation} forgeSqlOperations - The ForgeSQL operations instance for query analysis and execution
400
+ * @param {ForgeSqlOrmOptions} options - Configuration options for the ORM (e.g., logging settings)
333
401
  */
334
402
  constructor(forgeSqlOperations: ForgeSqlOperation, options: ForgeSqlOrmOptions) {
335
403
  this.forgeOperations = forgeSqlOperations;
@@ -337,10 +405,11 @@ export class Rovo implements RovoIntegration {
337
405
  }
338
406
 
339
407
  /**
340
- * Parses SQL query into AST and validates it's a single SELECT statement
341
- * @param sqlQuery - Normalized SQL query string
342
- * @returns Parsed AST of the SELECT statement
343
- * @throws Error if parsing fails or query is not a single SELECT statement
408
+ * Parses SQL query into AST and validates it's a single SELECT statement.
409
+ *
410
+ * @param {string} sqlQuery - Normalized SQL query string
411
+ * @returns {Select} Parsed AST of the SELECT statement
412
+ * @throws {Error} If parsing fails or query is not a single SELECT statement
344
413
  */
345
414
  private parseSqlQuery(sqlQuery: string): Select {
346
415
  const parser = new Parser();
@@ -370,9 +439,10 @@ export class Rovo implements RovoIntegration {
370
439
  }
371
440
 
372
441
  /**
373
- * Recursively processes array or single node and extracts tables
374
- * @param items - Array of nodes or single node
375
- * @param tables - Accumulator array for table names
442
+ * Recursively processes array or single node and extracts table names.
443
+ *
444
+ * @param {any} items - Array of AST nodes or single AST node
445
+ * @param {string[]} tables - Accumulator array for collecting table names (modified in place)
376
446
  */
377
447
  private extractTablesFromItems(items: any, tables: string[]): void {
378
448
  if (Array.isArray(items)) {
@@ -385,9 +455,10 @@ export class Rovo implements RovoIntegration {
385
455
  }
386
456
 
387
457
  /**
388
- * Extracts table name from table node
389
- * @param node - AST node with table information
390
- * @returns Table name in uppercase or null if not applicable
458
+ * Extracts table name from table AST node.
459
+ *
460
+ * @param {any} node - AST node with table information
461
+ * @returns {string | null} Table name in uppercase, or null if not applicable (e.g., 'dual' table)
391
462
  */
392
463
  private extractTableName(node: any): string | null {
393
464
  if (!node.table) {
@@ -398,9 +469,11 @@ export class Rovo implements RovoIntegration {
398
469
  }
399
470
 
400
471
  /**
401
- * Recursively extracts all table names from SQL AST node
402
- * @param node - AST node to extract tables from
403
- * @returns Array of table names (uppercase)
472
+ * Recursively extracts all table names from SQL AST node.
473
+ * Traverses FROM and JOIN clauses to find all referenced tables.
474
+ *
475
+ * @param {any} node - AST node to extract tables from
476
+ * @returns {string[]} Array of table names in uppercase
404
477
  */
405
478
  private extractTables(node: any): string[] {
406
479
  const tables: string[] = [];
@@ -427,9 +500,11 @@ export class Rovo implements RovoIntegration {
427
500
  }
428
501
 
429
502
  /**
430
- * Recursively checks if AST node contains scalar subqueries
431
- * @param node - AST node to check
432
- * @returns true if node contains scalar subquery, false otherwise
503
+ * Recursively checks if AST node contains scalar subqueries.
504
+ * Used for security validation to prevent subquery-based attacks.
505
+ *
506
+ * @param {any} node - AST node to check for subqueries
507
+ * @returns {boolean} True if node contains scalar subquery, false otherwise
433
508
  */
434
509
  private hasScalarSubquery(node: any): boolean {
435
510
  if (!node) return false;
@@ -452,13 +527,16 @@ export class Rovo implements RovoIntegration {
452
527
  /**
453
528
  * Creates a settings builder for Rovo queries using a raw table name.
454
529
  *
455
- * @param {string} tableName - The name of the table to query
456
- * @param {string} accountId - The account ID of the active user
530
+ * @param {string} tableName - The name of the table to query (case-insensitive)
531
+ * @param {string} accountId - The account ID of the active user for RLS filtering
457
532
  * @returns {RovoIntegrationSettingCreator} Builder for configuring Rovo query settings
458
533
  *
459
534
  * @example
460
535
  * ```typescript
461
536
  * const builder = rovo.rovoRawSettingBuilder('users', accountId);
537
+ * const settings = await builder
538
+ * .addStringContextParameter('{{status}}', 'active')
539
+ * .build();
462
540
  * ```
463
541
  */
464
542
  rovoRawSettingBuilder(tableName: string, accountId: string): RovoIntegrationSettingCreator {
@@ -467,14 +545,21 @@ export class Rovo implements RovoIntegration {
467
545
 
468
546
  /**
469
547
  * Creates a settings builder for Rovo queries using a Drizzle table object.
548
+ * This is a convenience method that extracts the table name from the Drizzle table object.
470
549
  *
471
550
  * @param {AnyMySqlTable} table - The Drizzle table object
472
- * @param {string} accountId - The account ID of the active user
551
+ * @param {string} accountId - The account ID of the active user for RLS filtering
473
552
  * @returns {RovoIntegrationSettingCreator} Builder for configuring Rovo query settings
474
553
  *
475
554
  * @example
476
555
  * ```typescript
477
556
  * const builder = rovo.rovoSettingBuilder(usersTable, accountId);
557
+ * const settings = await builder
558
+ * .useRLS()
559
+ * .addRlsColumn(usersTable.id)
560
+ * .addRlsWherePart((alias) => `${alias}.userId = '${accountId}'`)
561
+ * .finish()
562
+ * .build();
478
563
  * ```
479
564
  */
480
565
  rovoSettingBuilder(table: AnyMySqlTable, accountId: string): RovoIntegrationSettingCreator {
@@ -482,33 +567,12 @@ export class Rovo implements RovoIntegration {
482
567
  }
483
568
 
484
569
  /**
485
- * Executes a dynamic SQL query with comprehensive security validations.
486
- *
487
- * This method performs multiple security checks:
488
- * 1. Validates that the query is a SELECT statement
489
- * 2. Ensures the query targets only the specified table
490
- * 3. Blocks JOINs, subqueries, and window functions
491
- * 4. Applies Row-Level Security filtering if enabled
492
- * 5. Validates query results to ensure security fields are present
493
- *
494
- * @param {string} dynamicSql - The SQL query to execute (must be a SELECT statement)
495
- * @param {RovoIntegrationSetting} settings - Configuration settings for the query
496
- * @returns {Promise<Result<unknown>>} Query execution result with metadata
497
- * @throws {Error} If the query violates security restrictions
570
+ * Validates basic input parameters for the SQL query.
498
571
  *
499
- * @example
500
- * ```typescript
501
- * const result = await rovo.dynamicIsolatedQuery(
502
- * "SELECT id, name, email FROM users WHERE status = 'active' ORDER BY name",
503
- * settings
504
- * );
505
- *
506
- * console.log(result.rows); // Query results
507
- * console.log(result.metadata); // Query metadata
508
- * ```
509
- */
510
- /**
511
- * Validates basic input parameters
572
+ * @param {string} query - The SQL query string to validate
573
+ * @param {string} tableName - The expected table name
574
+ * @returns {string} The trimmed query string
575
+ * @throws {Error} If query is empty, table name is missing, or query is not a SELECT statement
512
576
  */
513
577
  private validateInputs(query: string, tableName: string): string {
514
578
  if (!query?.trim()) {
@@ -530,7 +594,12 @@ export class Rovo implements RovoIntegration {
530
594
  }
531
595
 
532
596
  /**
533
- * Normalizes SQL query using AST parsing and stringification
597
+ * Normalizes SQL query using AST parsing and stringification.
598
+ * This ensures consistent formatting and validates the query structure.
599
+ *
600
+ * @param {string} sql - The SQL query string to normalize
601
+ * @returns {string} The normalized SQL query string
602
+ * @throws {Error} If parsing fails, query is not a SELECT statement, or multiple statements are detected
534
603
  */
535
604
  private normalizeSqlString(sql: string): string {
536
605
  try {
@@ -566,7 +635,12 @@ export class Rovo implements RovoIntegration {
566
635
  }
567
636
 
568
637
  /**
569
- * Validates that query targets the correct table
638
+ * Validates that query targets the correct table.
639
+ * Checks that the FROM clause references only the expected table.
640
+ *
641
+ * @param {string} normalized - The normalized SQL query string
642
+ * @param {string} tableName - The expected table name
643
+ * @throws {Error} If query does not target the expected table
570
644
  */
571
645
  private validateTableName(normalized: string, tableName: string): void {
572
646
  const upperTableName = tableName.toUpperCase();
@@ -581,7 +655,12 @@ export class Rovo implements RovoIntegration {
581
655
  }
582
656
 
583
657
  /**
584
- * Validates query structure (tables, subqueries)
658
+ * Validates query structure for security compliance.
659
+ * Checks that only the specified table is referenced and no scalar subqueries are present.
660
+ *
661
+ * @param {Select} selectAst - The parsed SELECT AST node
662
+ * @param {string} tableName - The expected table name
663
+ * @throws {Error} If query references other tables or contains scalar subqueries
585
664
  */
586
665
  private validateQueryStructure(selectAst: Select, tableName: string): void {
587
666
  const upperTableName = tableName.toUpperCase();
@@ -616,7 +695,13 @@ export class Rovo implements RovoIntegration {
616
695
  }
617
696
 
618
697
  /**
619
- * Validates query execution plan for security violations
698
+ * Validates query execution plan for security violations.
699
+ * Uses EXPLAIN to detect JOINs, window functions, and references to other tables.
700
+ *
701
+ * @param {string} normalized - The normalized SQL query string
702
+ * @param {string} tableName - The expected table name
703
+ * @returns {Promise<void>}
704
+ * @throws {Error} If execution plan reveals JOINs, window functions, or references to other tables
620
705
  */
621
706
  private async validateExecutionPlan(normalized: string, tableName: string): Promise<void> {
622
707
  const explainRows = await this.forgeOperations.analyze().explainRaw(normalized, []);
@@ -667,7 +752,12 @@ export class Rovo implements RovoIntegration {
667
752
  }
668
753
 
669
754
  /**
670
- * Applies row-level security filtering to query
755
+ * Applies row-level security filtering to query.
756
+ * Wraps the original query in a subquery and adds a WHERE clause with RLS conditions.
757
+ *
758
+ * @param {string} normalized - The normalized SQL query string
759
+ * @param {RovoIntegrationSetting} settings - Rovo settings containing RLS configuration
760
+ * @returns {string} The SQL query with RLS filtering applied
671
761
  */
672
762
  private applyRLSFiltering(normalized: string, settings: RovoIntegrationSetting): string {
673
763
  if (normalized.endsWith(";")) {
@@ -684,7 +774,13 @@ export class Rovo implements RovoIntegration {
684
774
  }
685
775
 
686
776
  /**
687
- * Validates query results for RLS compliance
777
+ * Validates query results for RLS compliance.
778
+ * Ensures that required RLS fields are present and all fields originate from the correct table.
779
+ *
780
+ * @param {Result<unknown>} result - The query execution result
781
+ * @param {RovoIntegrationSetting} settings - Rovo settings containing RLS field requirements
782
+ * @param {string} upperTableName - The expected table name in uppercase
783
+ * @throws {Error} If required RLS fields are missing or fields originate from other tables
688
784
  */
689
785
  private validateQueryResults(
690
786
  result: Result<unknown>,
@@ -731,6 +827,32 @@ export class Rovo implements RovoIntegration {
731
827
  }
732
828
  }
733
829
 
830
+ /**
831
+ * Executes a dynamic SQL query with comprehensive security validations.
832
+ *
833
+ * This method performs multiple security checks:
834
+ * 1. Validates that the query is a SELECT statement
835
+ * 2. Ensures the query targets only the specified table
836
+ * 3. Blocks JOINs, subqueries, and window functions
837
+ * 4. Applies Row-Level Security filtering if enabled
838
+ * 5. Validates query results to ensure security fields are present
839
+ *
840
+ * @param {string} dynamicSql - The SQL query to execute (must be a SELECT statement)
841
+ * @param {RovoIntegrationSetting} settings - Configuration settings for the query
842
+ * @returns {Promise<Result<unknown>>} Query execution result with metadata
843
+ * @throws {Error} If the query violates security restrictions, parsing fails, or validation errors occur
844
+ *
845
+ * @example
846
+ * ```typescript
847
+ * const result = await rovo.dynamicIsolatedQuery(
848
+ * "SELECT id, name, email FROM users WHERE status = 'active' ORDER BY name",
849
+ * settings
850
+ * );
851
+ *
852
+ * console.log(result.rows); // Query results
853
+ * console.log(result.metadata); // Query metadata
854
+ * ```
855
+ */
734
856
  async dynamicIsolatedQuery(
735
857
  dynamicSql: string,
736
858
  settings: RovoIntegrationSetting,
@@ -0,0 +1,4 @@
1
+ export * from "./ForgeSQLQueryBuilder";
2
+ export * from "./ForgeSQLCrudOperations";
3
+ export * from "./ForgeSQLSelectOperations";
4
+ export * from "./SystemTables";
package/src/index.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  export { default } from "./core/ForgeSQLORM";
2
2
 
3
- export * from "./core/ForgeSQLQueryBuilder";
4
- export * from "./core/ForgeSQLCrudOperations";
5
- export * from "./core/ForgeSQLSelectOperations";
6
- export * from "./utils/sqlUtils";
7
- export * from "./utils/forgeDriver";
3
+ export * from "./core";
4
+
5
+ export * from "./utils";
6
+
8
7
  export * from "./webtriggers";
9
- export * from "./lib/drizzle/extensions/additionalActions";
10
- export * from "./core/SystemTables";
11
- export * from "./async/PrintQueryConsumer";
8
+
9
+ export * from "./lib";
10
+
11
+ export * from "./async";
@@ -0,0 +1 @@
1
+ export * from "./drizzle/extensions/additionalActions";
@@ -0,0 +1,2 @@
1
+ export * from "./sqlUtils";
2
+ export * from "./forgeDriver";
@@ -29,6 +29,8 @@ import { ForgeSqlOperation } from "../core/ForgeSQLQueryBuilder";
29
29
  import { ColumnDataType } from "drizzle-orm/column-builder";
30
30
  import { AnyMySqlColumn } from "drizzle-orm/mysql-core/columns/common";
31
31
  import type { ColumnBaseConfig } from "drizzle-orm/column";
32
+ import { getHttpResponse, TriggerResponse } from "../webtriggers";
33
+ import { getAppContext } from "@forge/api";
32
34
 
33
35
  /**
34
36
  * Interface representing table metadata information
@@ -969,3 +971,22 @@ export function withTidbHint<
969
971
  // but returning TExpr keeps the column type info in downstream inference.
970
972
  return sql`/*+ SET_VAR(tidb_session_alias=${sql.raw(SESSION_ALIAS_NAME_ORM)}) */ ${column}` as unknown as AnyMySqlColumn<TPartial>;
971
973
  }
974
+
975
+ /**
976
+ * Checks if the current environment is production and returns an error response if so.
977
+ * This function is used to prevent development-only operations from running in production.
978
+ *
979
+ * @param functionName - The name of the function being checked (for logging purposes)
980
+ * @returns {TriggerResponse<string> | null} Returns error response if in production, null otherwise
981
+ */
982
+ export function checkProductionEnvironment(functionName: string): TriggerResponse<string> | null {
983
+ const environmentType = getAppContext()?.environmentType;
984
+ if (environmentType === "PRODUCTION") {
985
+ // eslint-disable-next-line no-console
986
+ console.log(`${functionName} is disabled in production environment`);
987
+ return getHttpResponse<string>(500, `${functionName} is disabled in production environment`);
988
+ }
989
+ // eslint-disable-next-line no-console
990
+ console.log(`${functionName} triggered in ${environmentType}`);
991
+ return null;
992
+ }
@@ -7,6 +7,8 @@ import { ForgeSqlOrmOptions } from "../core/ForgeSQLQueryBuilder";
7
7
  * This trigger should be configured as a Forge scheduler to automatically
8
8
  * clean up expired cache entries based on their TTL (Time To Live).
9
9
  *
10
+ * @note This function is automatically disabled in production environments and will return a 500 error if called.
11
+ *
10
12
  * @param options - Optional ForgeSQL ORM configuration. If not provided,
11
13
  * uses default cache settings with cacheEntityName: "cache"
12
14
  * @returns Promise that resolves to HTTP response object
@@ -1,5 +1,8 @@
1
1
  import { sql } from "@forge/sql";
2
- import { generateDropTableStatements as generateStatements } from "../utils/sqlUtils";
2
+ import {
3
+ checkProductionEnvironment,
4
+ generateDropTableStatements as generateStatements,
5
+ } from "../utils/sqlUtils";
3
6
  import { getHttpResponse, TriggerResponse } from "./index";
4
7
  import { getTables } from "../core/SystemTables";
5
8
 
@@ -15,9 +18,11 @@ import { getTables } from "../core/SystemTables";
15
18
  * - It may affect application functionality
16
19
  * - It could lead to data loss and system instability
17
20
  *
21
+ * @note This function is automatically disabled in production environments and will return a 500 error if called.
22
+ *
18
23
  * @returns {Promise<TriggerResponse<string>>} A response containing:
19
24
  * - On success: 200 status with warning message about permanent deletion of tables and sequences
20
- * - On failure: 500 status with error message
25
+ * - On failure: 500 status with error message (including when called in production)
21
26
  *
22
27
  * @example
23
28
  * ```typescript
@@ -27,6 +32,10 @@ import { getTables } from "../core/SystemTables";
27
32
  * ```
28
33
  */
29
34
  export async function dropSchemaMigrations(): Promise<TriggerResponse<string>> {
35
+ const productionCheck = checkProductionEnvironment("dropSchemaMigrations");
36
+ if (productionCheck) {
37
+ return productionCheck;
38
+ }
30
39
  try {
31
40
  const tables = await getTables();
32
41
  // Generate drop statements
@@ -1,5 +1,8 @@
1
1
  import { sql } from "@forge/sql";
2
- import { generateDropTableStatements as generateStatements } from "../utils/sqlUtils";
2
+ import {
3
+ checkProductionEnvironment,
4
+ generateDropTableStatements as generateStatements,
5
+ } from "../utils/sqlUtils";
3
6
  import { getHttpResponse, TriggerResponse } from "./index";
4
7
  import { getTables } from "../core/SystemTables";
5
8
 
@@ -15,9 +18,11 @@ import { getTables } from "../core/SystemTables";
15
18
  * - It may affect application functionality
16
19
  * - It could lead to data loss and system instability
17
20
  *
21
+ * @note This function is automatically disabled in production environments and will return a 500 error if called.
22
+ *
18
23
  * @returns {Promise<TriggerResponse<string>>} A response containing:
19
24
  * - On success: 200 status with warning message about permanent deletion
20
- * - On failure: 500 status with error message
25
+ * - On failure: 500 status with error message (including when called in production)
21
26
  *
22
27
  * @example
23
28
  * ```typescript
@@ -27,6 +32,10 @@ import { getTables } from "../core/SystemTables";
27
32
  * ```
28
33
  */
29
34
  export async function dropTableSchemaMigrations(): Promise<TriggerResponse<string>> {
35
+ const productionCheck = checkProductionEnvironment("dropTableSchemaMigrations");
36
+ if (productionCheck) {
37
+ return productionCheck;
38
+ }
30
39
  try {
31
40
  const tables = await getTables();
32
41
  // Generate drop statements
@@ -2,6 +2,7 @@ import { sql } from "@forge/sql";
2
2
  import { getHttpResponse, TriggerResponse } from "./index";
3
3
  import { forgeSystemTables, getTables } from "../core/SystemTables";
4
4
  import { getTableName } from "drizzle-orm/table";
5
+ import { checkProductionEnvironment } from "../utils/sqlUtils";
5
6
 
6
7
  interface CreateTableRow {
7
8
  Table: string;
@@ -20,9 +21,11 @@ interface CreateTableRow {
20
21
  * - Generates SQL that could potentially be used maliciously
21
22
  * - May expose sensitive table names and structures
22
23
  *
24
+ * @note This function is automatically disabled in production environments and will return a 500 error if called.
25
+ *
23
26
  * @returns {Promise<TriggerResponse<string>>} A response containing SQL statements to recreate the database schema
24
27
  * - On success: Returns 200 status with SQL statements
25
- * - On failure: Returns 500 status with error message
28
+ * - On failure: Returns 500 status with error message (including when called in production)
26
29
  *
27
30
  * @example
28
31
  * ```typescript
@@ -34,6 +37,10 @@ interface CreateTableRow {
34
37
  * ```
35
38
  */
36
39
  export async function fetchSchemaWebTrigger(): Promise<TriggerResponse<string>> {
40
+ const productionCheck = checkProductionEnvironment("fetchSchemaWebTrigger");
41
+ if (productionCheck) {
42
+ return productionCheck;
43
+ }
37
44
  try {
38
45
  const tables = await getTables();
39
46
  const createTableStatements = await generateCreateTableStatements(tables);