forge-sql-orm 1.0.31 → 2.0.0

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 (37) hide show
  1. package/README.md +216 -695
  2. package/dist/ForgeSQLORM.js +526 -564
  3. package/dist/ForgeSQLORM.js.map +1 -1
  4. package/dist/ForgeSQLORM.mjs +527 -554
  5. package/dist/ForgeSQLORM.mjs.map +1 -1
  6. package/dist/core/ForgeSQLCrudOperations.d.ts +101 -130
  7. package/dist/core/ForgeSQLCrudOperations.d.ts.map +1 -1
  8. package/dist/core/ForgeSQLORM.d.ts +11 -10
  9. package/dist/core/ForgeSQLORM.d.ts.map +1 -1
  10. package/dist/core/ForgeSQLQueryBuilder.d.ts +275 -111
  11. package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
  12. package/dist/core/ForgeSQLSelectOperations.d.ts +65 -22
  13. package/dist/core/ForgeSQLSelectOperations.d.ts.map +1 -1
  14. package/dist/core/SystemTables.d.ts +59 -0
  15. package/dist/core/SystemTables.d.ts.map +1 -0
  16. package/dist/index.d.ts +1 -2
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/utils/sqlUtils.d.ts +53 -6
  19. package/dist/utils/sqlUtils.d.ts.map +1 -1
  20. package/dist-cli/cli.js +461 -397
  21. package/dist-cli/cli.js.map +1 -1
  22. package/dist-cli/cli.mjs +461 -397
  23. package/dist-cli/cli.mjs.map +1 -1
  24. package/package.json +21 -27
  25. package/src/core/ForgeSQLCrudOperations.ts +360 -473
  26. package/src/core/ForgeSQLORM.ts +38 -79
  27. package/src/core/ForgeSQLQueryBuilder.ts +255 -132
  28. package/src/core/ForgeSQLSelectOperations.ts +185 -72
  29. package/src/core/SystemTables.ts +7 -0
  30. package/src/index.ts +1 -2
  31. package/src/utils/sqlUtils.ts +164 -22
  32. package/dist/core/ComplexQuerySchemaBuilder.d.ts +0 -38
  33. package/dist/core/ComplexQuerySchemaBuilder.d.ts.map +0 -1
  34. package/dist/knex/index.d.ts +0 -4
  35. package/dist/knex/index.d.ts.map +0 -1
  36. package/src/core/ComplexQuerySchemaBuilder.ts +0 -63
  37. package/src/knex/index.ts +0 -4
@@ -1,8 +1,3 @@
1
- import type { EntityName, LoggingOptions } from "..";
2
- import type { EntitySchema } from "@mikro-orm/core/metadata/EntitySchema";
3
- import type { AnyEntity, EntityClass, EntityClassGroup } from "@mikro-orm/core/typings";
4
- import type { QueryBuilder } from "@mikro-orm/knex/query";
5
- import { MemoryCacheAdapter, MikroORM, NullCacheAdapter } from "@mikro-orm/mysql";
6
1
  import { ForgeSQLCrudOperations } from "./ForgeSQLCrudOperations";
7
2
  import {
8
3
  CRUDForgeSQL,
@@ -11,52 +6,36 @@ import {
11
6
  SchemaSqlForgeSql,
12
7
  } from "./ForgeSQLQueryBuilder";
13
8
  import { ForgeSQLSelectOperations } from "./ForgeSQLSelectOperations";
14
- import type { Knex } from "knex";
9
+ import { drizzle } from "drizzle-orm/mysql2";
10
+ import { MySql2Database } from "drizzle-orm/mysql2/driver";
15
11
 
16
12
  /**
17
- * Implementation of ForgeSQLORM that interacts with MikroORM.
13
+ * Implementation of ForgeSQLORM that uses Drizzle ORM for query building.
14
+ * This class provides a bridge between Forge SQL and Drizzle ORM, allowing
15
+ * to use Drizzle's query builder while executing queries through Forge SQL.
18
16
  */
19
17
  class ForgeSQLORMImpl implements ForgeSqlOperation {
20
18
  private static instance: ForgeSQLORMImpl | null = null;
21
- private readonly mikroORM: MikroORM;
19
+ private readonly drizzle: MySql2Database<Record<string, unknown>>;
22
20
  private readonly crudOperations: CRUDForgeSQL;
23
21
  private readonly fetchOperations: SchemaSqlForgeSql;
24
22
 
25
23
  /**
26
24
  * Private constructor to enforce singleton behavior.
27
- * @param entities - The list of entities for ORM initialization.
28
25
  * @param options - Options for configuring ForgeSQL ORM behavior.
29
26
  */
30
- private constructor(
31
- entities: (EntityClass<AnyEntity> | EntityClassGroup<AnyEntity> | EntitySchema)[],
32
- options?: ForgeSqlOrmOptions,
33
- ) {
34
-
27
+ private constructor(options?: ForgeSqlOrmOptions) {
35
28
  try {
36
- const newOptions: ForgeSqlOrmOptions = options ?? { logRawSqlQuery: false, disableOptimisticLocking: false };
37
- if (newOptions.logRawSqlQuery){
29
+ const newOptions: ForgeSqlOrmOptions = options ?? {
30
+ logRawSqlQuery: false,
31
+ disableOptimisticLocking: false,
32
+ };
33
+ if (newOptions.logRawSqlQuery) {
38
34
  console.debug("Initializing ForgeSQLORM...");
39
35
  }
40
- this.mikroORM = MikroORM.initSync({
41
- dbName: "inmemory",
42
- schemaGenerator: {
43
- disableForeignKeys: false,
44
- },
45
- discovery: {
46
- warnWhenNoEntities: true,
47
- },
48
- resultCache: {
49
- adapter: NullCacheAdapter,
50
- },
51
- metadataCache: {
52
- enabled: false,
53
- adapter: MemoryCacheAdapter,
54
- },
55
- entities: entities,
56
- preferTs: false,
57
- debug: false,
58
- });
59
-
36
+ // Initialize Drizzle instance for query building only
37
+ // This instance should not be used for direct database connections
38
+ this.drizzle = drizzle("");
60
39
  this.crudOperations = new ForgeSQLCrudOperations(this, newOptions);
61
40
  this.fetchOperations = new ForgeSQLSelectOperations(newOptions);
62
41
  } catch (error) {
@@ -67,16 +46,12 @@ class ForgeSQLORMImpl implements ForgeSqlOperation {
67
46
 
68
47
  /**
69
48
  * Returns the singleton instance of ForgeSQLORMImpl.
70
- * @param entities - List of entities (required only on first initialization).
71
49
  * @param options - Options for configuring ForgeSQL ORM behavior.
72
50
  * @returns The singleton instance of ForgeSQLORMImpl.
73
51
  */
74
- static getInstance(
75
- entities: (EntityClass<AnyEntity> | EntityClassGroup<AnyEntity> | EntitySchema)[],
76
- options?: ForgeSqlOrmOptions,
77
- ): ForgeSqlOperation {
52
+ static getInstance(options?: ForgeSqlOrmOptions): ForgeSqlOperation {
78
53
  if (!ForgeSQLORMImpl.instance) {
79
- ForgeSQLORMImpl.instance = new ForgeSQLORMImpl(entities, options);
54
+ ForgeSQLORMImpl.instance = new ForgeSQLORMImpl(options);
80
55
  }
81
56
  return ForgeSQLORMImpl.instance;
82
57
  }
@@ -98,41 +73,28 @@ class ForgeSQLORMImpl implements ForgeSqlOperation {
98
73
  }
99
74
 
100
75
  /**
101
- * Creates a new query builder for the given entity.
102
- * @param entityName - The entity name or an existing query builder.
103
- * @param alias - The alias for the entity.
104
- * @param loggerContext - Logging options.
105
- * @returns The query builder instance.
106
- */
107
- createQueryBuilder<Entity extends object, RootAlias extends string = never>(
108
- entityName: EntityName<Entity> | QueryBuilder<Entity>,
109
- alias?: RootAlias,
110
- loggerContext?: LoggingOptions,
111
- ): QueryBuilder<Entity, RootAlias> {
112
- return this.mikroORM.em.createQueryBuilder(entityName, alias, undefined, loggerContext);
113
- }
114
-
115
- /**
116
- * Provides access to the underlying Knex instance for building complex query parts.
117
- * enabling advanced query customization and performance tuning.
118
- * @returns The Knex instance, which can be used for query building.
76
+ * Returns a Drizzle query builder instance.
77
+ *
78
+ * ⚠️ IMPORTANT: This method should be used ONLY for query building purposes.
79
+ * The returned instance should NOT be used for direct database connections or query execution.
80
+ * All database operations should be performed through Forge SQL's executeRawSQL or executeRawUpdateSQL methods.
81
+ *
82
+ * @returns A Drizzle query builder instance for query construction only.
119
83
  */
120
- getKnex(): Knex<any, any[]> {
121
- return this.mikroORM.em.getKnex();
84
+ getDrizzleQueryBuilder(): MySql2Database<Record<string, unknown>> {
85
+ return this.drizzle;
122
86
  }
123
87
  }
124
88
 
125
89
  /**
126
90
  * Public class that acts as a wrapper around the private ForgeSQLORMImpl.
91
+ * Provides a clean interface for working with Forge SQL and Drizzle ORM.
127
92
  */
128
93
  class ForgeSQLORM {
129
94
  private readonly ormInstance: ForgeSqlOperation;
130
95
 
131
- constructor(
132
- entities: (EntityClass<AnyEntity> | EntityClassGroup<AnyEntity> | EntitySchema)[],
133
- options?: ForgeSqlOrmOptions,
134
- ) {
135
- this.ormInstance = ForgeSQLORMImpl.getInstance(entities, options);
96
+ constructor(options?: ForgeSqlOrmOptions) {
97
+ this.ormInstance = ForgeSQLORMImpl.getInstance(options);
136
98
  }
137
99
 
138
100
  /**
@@ -151,20 +113,17 @@ class ForgeSQLORM {
151
113
  return this.ormInstance.fetch();
152
114
  }
153
115
 
154
- getKnex(): Knex<any, any[]> {
155
- return this.ormInstance.getKnex();
156
- }
157
-
158
116
  /**
159
- * Proxies the `createQueryBuilder` method from `ForgeSQLORMImpl`.
160
- * @returns A new query builder instance.
117
+ * Returns a Drizzle query builder instance.
118
+ *
119
+ * ⚠️ IMPORTANT: This method should be used ONLY for query building purposes.
120
+ * The returned instance should NOT be used for direct database connections or query execution.
121
+ * All database operations should be performed through Forge SQL's executeRawSQL or executeRawUpdateSQL methods.
122
+ *
123
+ * @returns A Drizzle query builder instance for query construction only.
161
124
  */
162
- createQueryBuilder<Entity extends object, RootAlias extends string = never>(
163
- entityName: EntityName<Entity> | QueryBuilder<Entity>,
164
- alias?: RootAlias,
165
- loggerContext?: LoggingOptions,
166
- ): QueryBuilder<Entity, RootAlias> {
167
- return this.ormInstance.createQueryBuilder(entityName, alias, loggerContext);
125
+ getDrizzleQueryBuilder(): MySql2Database<Record<string, unknown>> {
126
+ return this.ormInstance.getDrizzleQueryBuilder();
168
127
  }
169
128
  }
170
129
 
@@ -1,197 +1,320 @@
1
1
  import { UpdateQueryResponse } from "@forge/sql";
2
- import type { EntityName, LoggingOptions, QBFilterQuery } from "..";
3
- import type { EntitySchema } from "@mikro-orm/core/metadata/EntitySchema";
4
- import type { QueryBuilder } from "@mikro-orm/knex/query";
5
- import type { Knex } from "knex";
6
- import {EntityKey, EntityProperty} from "@mikro-orm/core";
7
- import {SqlParameters} from "@forge/sql/out/sql-statement";
8
- import {DynamicEntity} from "./ComplexQuerySchemaBuilder";
2
+ import { SqlParameters } from "@forge/sql/out/sql-statement";
3
+ import { MySql2Database } from "drizzle-orm/mysql2/driver";
4
+ import { AnyMySqlSelectQueryBuilder, AnyMySqlTable, customType } from "drizzle-orm/mysql-core";
5
+ import { MySqlSelectDynamic } from "drizzle-orm/mysql-core/query-builders/select.types";
6
+ import { InferInsertModel, SQL } from "drizzle-orm";
7
+ import moment from "moment/moment";
8
+ import { parseDateTime } from "../utils/sqlUtils";
9
+
10
+ // ============= Core Types =============
9
11
 
10
12
  /**
11
13
  * Interface representing the main ForgeSQL operations.
14
+ * Provides access to CRUD operations and schema-level SQL operations.
12
15
  */
13
16
  export interface ForgeSqlOperation extends QueryBuilderForgeSql {
14
17
  /**
15
- * Provides CRUD operations.
18
+ * Provides CRUD (Create, Read, Update, Delete) operations.
19
+ * @returns {CRUDForgeSQL} Interface for performing CRUD operations
16
20
  */
17
21
  crud(): CRUDForgeSQL;
18
22
 
19
23
  /**
20
24
  * Provides schema-level SQL fetch operations.
25
+ * @returns {SchemaSqlForgeSql} Interface for executing schema-bound SQL queries
21
26
  */
22
27
  fetch(): SchemaSqlForgeSql;
23
28
  }
24
29
 
25
30
  /**
26
- * Options for configuring ForgeSQL ORM behavior.
31
+ * Interface for Query Builder operations.
32
+ * Provides access to the underlying Drizzle ORM query builder.
27
33
  */
28
- export interface ForgeSqlOrmOptions {
29
- /**
30
- * Enables logging of raw SQL queries in the Atlassian Forge Developer Console.
31
- */
32
- logRawSqlQuery?: boolean;
34
+ export interface QueryBuilderForgeSql {
33
35
  /**
34
- * Disable optimistic locking
36
+ * Creates a new query builder for the given entity.
37
+ * @returns {MySql2Database<Record<string, unknown>>} The Drizzle database instance for building queries
35
38
  */
36
- disableOptimisticLocking?: boolean
39
+ getDrizzleQueryBuilder(): MySql2Database<Record<string, unknown>>;
37
40
  }
38
41
 
42
+ // ============= CRUD Operations =============
43
+
39
44
  /**
40
- * Interface for schema-level SQL operations.
45
+ * Interface for CRUD (Create, Read, Update, Delete) operations.
46
+ * Provides methods for basic database operations with support for optimistic locking.
41
47
  */
42
- export interface SchemaSqlForgeSql {
43
- /**
44
- * Executes a schema-bound SQL query and maps the result to the specified entity schema.
45
- * @param query - The SQL query to execute.
46
- * @param schema - The entity schema.
47
- * @returns A list of mapped entity objects.
48
- */
49
- executeSchemaSQL<T extends object>(query: string, schema: EntitySchema<T>): Promise<T[]>;
50
-
48
+ export interface CRUDForgeSQL {
51
49
  /**
52
- * Executes a schema-bound SQL query and maps the only one result to the specified entity schema.
53
- * @param query - The SQL query to execute.
54
- * @param schema - The entity schema.
55
- * @returns A list of mapped entity objects.
50
+ * Inserts multiple records into the database.
51
+ * @template T - The type of the table schema
52
+ * @param {T} schema - The entity schema
53
+ * @param {Partial<InferInsertModel<T>>[]} models - The list of entities to insert
54
+ * @param {boolean} [updateIfExists] - Whether to update the row if it already exists (default: false)
55
+ * @returns {Promise<number>} The number of inserted rows
56
+ * @throws {Error} If the insert operation fails
56
57
  */
57
- executeSchemaSQLOnlyOne<T extends object>(query: string, schema: EntitySchema<T>): Promise<T|undefined>;
58
+ insert<T extends AnyMySqlTable>(
59
+ schema: T,
60
+ models: Partial<InferInsertModel<T>>[],
61
+ updateIfExists?: boolean,
62
+ ): Promise<number>;
58
63
 
59
64
  /**
60
- * Executes a raw SQL query and returns the results.
61
- * @param query - The raw SQL query.
62
- * @returns A list of results as objects.
65
+ * Deletes a record by its ID.
66
+ * @template T - The type of the table schema
67
+ * @param {unknown} id - The ID of the record to delete
68
+ * @param {T} schema - The entity schema
69
+ * @returns {Promise<number>} The number of rows affected
70
+ * @throws {Error} If the delete operation fails
63
71
  */
64
- executeRawSQL<T extends object | unknown>(query: string): Promise<T[]>;
72
+ deleteById<T extends AnyMySqlTable>(id: unknown, schema: T): Promise<number>;
65
73
 
66
74
  /**
67
- * Executes a raw SQL update query.
68
- * @param query - The raw SQL update query.
69
- * @param params - Sql parameters.
70
- * @returns The update response containing affected rows.
75
+ * Updates a record by its ID with optimistic locking support.
76
+ * If a version field is defined in the schema, versioning is applied:
77
+ * - the current record version is retrieved
78
+ * - checked for concurrent modifications
79
+ * - and then incremented
80
+ *
81
+ * @template T - The type of the table schema
82
+ * @param {Partial<InferInsertModel<T>>} entity - The entity with updated values
83
+ * @param {T} schema - The entity schema
84
+ * @returns {Promise<number>} The number of rows affected
85
+ * @throws {Error} If the primary key is not included in the update fields
86
+ * @throws {Error} If optimistic locking check fails
71
87
  */
72
- executeRawUpdateSQL(query: string, params?: SqlParameters[]): Promise<UpdateQueryResponse>;
88
+ updateById<T extends AnyMySqlTable>(
89
+ entity: Partial<InferInsertModel<T>>,
90
+ schema: T,
91
+ ): Promise<number>;
73
92
 
74
93
  /**
75
- * Creates a builder for constructing complex query schemas dynamically.
76
- * This method is useful when working with dynamic entity structures where fields
77
- * may not be known at compile time.
78
- * @returns An instance of ComplexQuerySchemaBuilder configured for dynamic entities.
94
+ * Updates specified fields of records based on provided conditions.
95
+ * If the "where" parameter is not provided, the WHERE clause is built from the entity fields
96
+ * that are not included in the list of fields to update.
97
+ *
98
+ * @template T - The type of the table schema
99
+ * @param {Partial<InferInsertModel<T>>} updateData - The object containing values to update
100
+ * @param {T} schema - The entity schema
101
+ * @param {SQL<unknown>} [where] - Optional filtering conditions for the WHERE clause
102
+ * @returns {Promise<number>} The number of affected rows
103
+ * @throws {Error} If no filtering criteria are provided
104
+ * @throws {Error} If the update operation fails
79
105
  */
80
- createComplexQuerySchema():ComplexQuerySchemaBuilder<DynamicEntity>
106
+ updateFields<T extends AnyMySqlTable>(
107
+ updateData: Partial<InferInsertModel<T>>,
108
+ schema: T,
109
+ where?: SQL<unknown>,
110
+ ): Promise<number>;
81
111
  }
82
112
 
113
+ // ============= Schema SQL Operations =============
114
+
83
115
  /**
84
- * Interface for CRUD (Create, Read, Update, Delete) operations.
116
+ * Interface for schema-level SQL operations.
117
+ * Provides methods for executing SQL queries with schema binding and type safety.
85
118
  */
86
- export interface CRUDForgeSQL {
119
+ export interface SchemaSqlForgeSql {
87
120
  /**
88
- * Inserts multiple records into the database.
89
- * @param schema - The entity schema.
90
- * @param models - The list of entities to insert.
91
- * @param updateIfExists - Whether to update the row if it already exists.
92
- * @returns The number of inserted rows.
121
+ * Executes a Drizzle query and returns the result.
122
+ * @template T - The type of the query builder
123
+ * @param {T} query - The Drizzle query to execute
124
+ * @returns {Promise<Awaited<T>>} The query result
125
+ * @throws {Error} If the query execution fails
93
126
  */
94
- insert<T extends object>(
95
- schema: EntitySchema<T>,
96
- models: T[],
97
- updateIfExists?: boolean,
98
- ): Promise<number>;
127
+ executeQuery<T extends MySqlSelectDynamic<AnyMySqlSelectQueryBuilder>>(
128
+ query: T,
129
+ ): Promise<Awaited<T>>;
99
130
 
100
131
  /**
101
- * Deletes a record by its ID.
102
- * @param id - The ID of the record to delete.
103
- * @param schema - The entity schema.
104
- * @returns The number of rows affected.
132
+ * Executes a Drizzle query and returns a single result.
133
+ * @template T - The type of the query builder
134
+ * @param {T} query - The Drizzle query to execute
135
+ * @returns {Promise<Awaited<T> extends Array<any> ? Awaited<T>[number] | undefined : Awaited<T> | undefined>} A single result object or undefined
136
+ * @throws {Error} If more than one record is returned
137
+ * @throws {Error} If the query execution fails
105
138
  */
106
- deleteById<T extends object>(id: unknown, schema: EntitySchema<T>): Promise<number>;
139
+ executeQueryOnlyOne<T extends MySqlSelectDynamic<AnyMySqlSelectQueryBuilder>>(
140
+ query: T,
141
+ ): Promise<
142
+ Awaited<T> extends Array<any> ? Awaited<T>[number] | undefined : Awaited<T> | undefined
143
+ >;
107
144
 
108
145
  /**
109
- * Updates a record by its ID.
110
- * * If a version field is defined in the schema, versioning is applied:
111
- * * the current record version is retrieved, checked for concurrent modifications,
112
- * * and then incremented.
113
- * *
114
- * * @param entity - The entity with updated values.
115
- * * @param schema - The entity schema.
116
- * * @throws If the primary key is not included in the update fields.
117
- * */
118
- updateById<T extends object>(entity: Partial<T>, schema: EntitySchema<T>): Promise<void>;
119
-
120
- /**
121
- * Updates specified fields of records based on provided conditions.
122
- * If the "where" parameter is not provided, the WHERE clause is built from the entity fields
123
- * that are not included in the list of fields to update.
124
- *
125
- * @param entity - The object containing values to update and potential criteria for filtering.
126
- * @param fields - Array of field names to update.
127
- * @param schema - The entity schema.
128
- * @param where - Optional filtering conditions for the WHERE clause.
129
- * @returns The number of affected rows.
130
- * @throws If no filtering criteria are provided (either via "where" or from the remaining entity fields).
146
+ * Executes a raw SQL query and returns the results.
147
+ * @template T - The type of the result objects
148
+ * @param {string} query - The raw SQL query
149
+ * @param {SqlParameters[]} [params] - Optional SQL parameters
150
+ * @returns {Promise<T[]>} A list of results as objects
151
+ * @throws {Error} If the query execution fails
131
152
  */
132
- updateFields<T extends object>(
133
- entity: Partial<T>,
134
- fields: EntityKey<T>[],
135
- schema: EntitySchema<T>,
136
- where?: QBFilterQuery<T>,
137
- ): Promise<number>
153
+ executeRawSQL<T extends object | unknown>(query: string, params?: SqlParameters[]): Promise<T[]>;
138
154
 
139
155
  /**
140
- * Updates specific fields of a record identified by its primary key.
141
- *
142
- * If a version field is defined in the schema, versioning is applied:
143
- * the current record version is retrieved, checked for concurrent modifications,
144
- * and then incremented.
145
- *
146
- * @param entity - The entity with updated values.
147
- * @param fields - The list of field names to update.
148
- * @param schema - The entity schema.
149
- * @throws If the primary key is not included in the update fields.
156
+ * Executes a raw SQL update query.
157
+ * @param {string} query - The raw SQL update query
158
+ * @param {SqlParameters[]} [params] - Optional SQL parameters
159
+ * @returns {Promise<UpdateQueryResponse>} The update response containing affected rows
160
+ * @throws {Error} If the update operation fails
150
161
  */
151
- updateFieldById<T extends object>(
152
- entity: T,
153
- fields: EntityKey<T>[],
154
- schema: EntitySchema<T>,
155
- ): Promise<void>;
162
+ executeRawUpdateSQL(query: string, params?: unknown[]): Promise<UpdateQueryResponse>;
156
163
  }
157
164
 
165
+ // ============= Configuration Types =============
166
+
158
167
  /**
159
- * Interface for Query Builder operations.
168
+ * Interface for version field metadata.
169
+ * Defines the configuration for optimistic locking version fields.
160
170
  */
161
- export interface QueryBuilderForgeSql {
171
+ export interface VersionFieldMetadata {
172
+ /** Name of the version field */
173
+ fieldName: string;
174
+ }
175
+
176
+ /**
177
+ * Interface for table metadata.
178
+ * Defines the configuration for a specific table.
179
+ */
180
+ export interface TableMetadata {
181
+ /** Name of the table */
182
+ tableName: string;
183
+ /** Version field configuration for optimistic locking */
184
+ versionField: VersionFieldMetadata;
185
+ }
186
+
187
+ /**
188
+ * Type for additional metadata configuration.
189
+ * Maps table names to their metadata configuration.
190
+ */
191
+ export type AdditionalMetadata = Record<string, TableMetadata>;
192
+
193
+ /**
194
+ * Options for configuring ForgeSQL ORM behavior.
195
+ */
196
+ export interface ForgeSqlOrmOptions {
162
197
  /**
163
- * Creates a new query builder for the given entity.
164
- * @param entityName - The entity name or an existing query builder.
165
- * @param alias - The alias for the entity.
166
- * @param loggerContext - Logging options.
167
- * @returns The query builder instance.
198
+ * Enables logging of raw SQL queries in the Atlassian Forge Developer Console.
199
+ * Useful for debugging and monitoring SQL operations.
200
+ * @default false
168
201
  */
169
- createQueryBuilder<Entity extends object, RootAlias extends string = never>(
170
- entityName: EntityName<Entity> | QueryBuilder<Entity>,
171
- alias?: RootAlias,
172
- loggerContext?: LoggingOptions,
173
- ): QueryBuilder<Entity, RootAlias>;
174
-
202
+ logRawSqlQuery?: boolean;
175
203
  /**
176
- * Provides access to the underlying Knex instance for building complex query parts.
177
- * enabling advanced query customization and performance tuning.
178
- * @returns The Knex instance, which can be used for query building.
204
+ * Enables logging of raw SQL queries in the Atlassian Forge Developer Console.
205
+ * Useful for debugging and monitoring SQL operations.
206
+ * @default false
179
207
  */
180
- getKnex(): Knex<any, any[]>;
181
- }
208
+ logRawSqlQueryParams?: boolean;
182
209
 
183
- export interface ComplexQuerySchemaBuilder<T> {
184
210
  /**
185
- * Adds a field from an entity schema to the builder.
186
- * @param field - The entity property to be added.
187
- * @param alias - (Optional) Alias for the field name.
188
- * @returns The updated instance of the builder.
211
+ * Disables optimistic locking for all operations.
212
+ * When enabled, version checks are skipped during updates.
213
+ * @default false
189
214
  */
190
- addField<K>(field: Partial<EntityProperty<K>>, alias?: string): this
215
+ disableOptimisticLocking?: boolean;
191
216
 
192
217
  /**
193
- * Creates and returns a new entity schema based on the added fields.
194
- * @returns A new EntitySchema<T> instance.
218
+ * Additional metadata for table configuration.
219
+ * Allows specifying table-specific settings and behaviors.
220
+ * @example
221
+ * ```typescript
222
+ * {
223
+ * users: {
224
+ * tableName: "users",
225
+ * versionField: {
226
+ * fieldName: "updatedAt",
227
+ * type: "datetime",
228
+ * nullable: false
229
+ * }
230
+ * }
231
+ * }
232
+ * ```
195
233
  */
196
- createSchema(): EntitySchema<T>;
234
+ additionalMetadata?: AdditionalMetadata;
197
235
  }
236
+
237
+ // ============= Custom Types =============
238
+
239
+ /**
240
+ * Custom type for MySQL datetime fields.
241
+ * Handles conversion between JavaScript Date objects and MySQL datetime strings.
242
+ */
243
+ export const mySqlDateTimeString = customType<{
244
+ data: Date;
245
+ driver: string;
246
+ config: { format?: string };
247
+ }>({
248
+ dataType() {
249
+ return "datetime";
250
+ },
251
+ toDriver(value: Date) {
252
+ return moment(value as Date).format("YYYY-MM-DDTHH:mm:ss.SSS");
253
+ },
254
+ fromDriver(value: unknown) {
255
+ const format = "YYYY-MM-DDTHH:mm:ss.SSS";
256
+ return parseDateTime(value as string, format);
257
+ },
258
+ });
259
+
260
+ /**
261
+ * Custom type for MySQL timestamp fields.
262
+ * Handles conversion between JavaScript Date objects and MySQL timestamp strings.
263
+ */
264
+ export const mySqlTimestampString = customType<{
265
+ data: Date;
266
+ driver: string;
267
+ config: { format?: string };
268
+ }>({
269
+ dataType() {
270
+ return "timestamp";
271
+ },
272
+ toDriver(value: Date) {
273
+ return moment(value as Date).format("YYYY-MM-DDTHH:mm:ss.SSS");
274
+ },
275
+ fromDriver(value: unknown) {
276
+ const format = "YYYY-MM-DDTHH:mm:ss.SSS";
277
+ return parseDateTime(value as string, format);
278
+ },
279
+ });
280
+
281
+ /**
282
+ * Custom type for MySQL date fields.
283
+ * Handles conversion between JavaScript Date objects and MySQL date strings.
284
+ */
285
+ export const mySqlDateString = customType<{
286
+ data: Date;
287
+ driver: string;
288
+ config: { format?: string };
289
+ }>({
290
+ dataType() {
291
+ return "date";
292
+ },
293
+ toDriver(value: Date) {
294
+ return moment(value as Date).format("YYYY-MM-DD");
295
+ },
296
+ fromDriver(value: unknown) {
297
+ const format = "YYYY-MM-DD";
298
+ return parseDateTime(value as string, format);
299
+ },
300
+ });
301
+
302
+ /**
303
+ * Custom type for MySQL time fields.
304
+ * Handles conversion between JavaScript Date objects and MySQL time strings.
305
+ */
306
+ export const mySqlTimeString = customType<{
307
+ data: Date;
308
+ driver: string;
309
+ config: { format?: string };
310
+ }>({
311
+ dataType() {
312
+ return "time";
313
+ },
314
+ toDriver(value: Date) {
315
+ return moment(value as Date).format("HH:mm:ss.SSS");
316
+ },
317
+ fromDriver(value: unknown) {
318
+ return parseDateTime(value as string, "HH:mm:ss.SSS");
319
+ },
320
+ });