• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vzakharchenko / forge-sql-orm / 17695420824

13 Sep 2025 10:37AM UTC coverage: 80.749% (-0.05%) from 80.8%
17695420824

push

github

vzakharchenko
fixed date issues

364 of 445 branches covered (81.8%)

Branch coverage included in aggregate %.

9 of 13 new or added lines in 2 files covered. (69.23%)

1641 of 2038 relevant lines covered (80.52%)

11.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

72.41
/src/core/ForgeSQLQueryBuilder.ts
1
import { UpdateQueryResponse } from "@forge/sql";
2
import { SqlParameters } from "@forge/sql/out/sql-statement";
3
import {
1✔
4
  AnyMySqlSelectQueryBuilder,
5
  AnyMySqlTable,
6
  customType,
7
  MySqlSelectBuilder,
8
  MySqlTable,
9
} from "drizzle-orm/mysql-core";
10
import {
11
  MySqlSelectDynamic,
12
  type SelectedFields,
13
} from "drizzle-orm/mysql-core/query-builders/select.types";
14
import { InferInsertModel, Query, SQL } from "drizzle-orm";
15
import { parseDateTime, formatDateTime } from "../utils/sqlUtils";
1✔
16
import { MySqlRemoteDatabase, MySqlRemotePreparedQueryHKT } from "drizzle-orm/mysql-proxy";
17
import { SqlHints } from "../utils/sqlHints";
18
import {
19
  ClusterStatementRowCamelCase,
20
  ExplainAnalyzeRow,
21
  SlowQueryNormalized,
22
} from "./SystemTables";
23
import { ForgeSQLCacheOperations } from "./ForgeSQLCacheOperations";
24
import {
25
  DeleteAndEvictCacheType,
26
  InsertAndEvictCacheType,
27
  SelectAliasedCacheableType,
28
  SelectAliasedDistinctCacheableType,
29
  SelectAliasedDistinctType,
30
  SelectAliasedType,
31
  UpdateAndEvictCacheType,
32
} from "..";
33
import {
34
  MySqlDeleteBase,
35
  MySqlInsertBuilder,
36
  MySqlUpdateBuilder,
37
} from "drizzle-orm/mysql-core/query-builders";
38
import { MySqlRemoteQueryResultHKT } from "drizzle-orm/mysql-proxy";
39
/**
40
 * Core interface for ForgeSQL operations.
41
 * Provides access to CRUD operations, schema-level SQL operations, and query analysis capabilities.
42
 *
43
 * This is the main interface that developers interact with when using ForgeSQL ORM.
44
 * It combines query building capabilities with database operations and caching.
45
 *
46
 * @interface ForgeSqlOperation
47
 * @extends {QueryBuilderForgeSql}
48
 */
49
export interface ForgeSqlOperation extends QueryBuilderForgeSql {
50
  /**
51
   * Creates a new query builder for the given entity.
52
   * @returns {MySqlRemoteDatabase<Record<string, unknown>>} The Drizzle database instance for building queries
53
   */
54
  getDrizzleQueryBuilder(): MySqlRemoteDatabase<Record<string, unknown>> & {
55
    selectAliased: SelectAliasedType;
56
    selectAliasedDistinct: SelectAliasedDistinctType;
57
    selectAliasedCacheable: SelectAliasedCacheableType;
58
    selectAliasedDistinctCacheable: SelectAliasedDistinctCacheableType;
59
    insertWithCacheContext: InsertAndEvictCacheType;
60
    insertAndEvictCache: InsertAndEvictCacheType;
61
    updateAndEvictCache: UpdateAndEvictCacheType;
62
    updateWithCacheContext: UpdateAndEvictCacheType;
63
    deleteAndEvictCache: DeleteAndEvictCacheType;
64
    deleteWithCacheContext: DeleteAndEvictCacheType;
65
  };
66

67
  /**
68
   * Provides modify (Create, Update, Delete) operations with optimistic locking support.
69
   * @returns {VerioningModificationForgeSQL} Interface for performing CRUD operations
70
   */
71
  modifyWithVersioning(): VerioningModificationForgeSQL;
72

73
  /**
74
   * Provides schema-level SQL fetch operations with type safety.
75
   * @returns {SchemaSqlForgeSql} Interface for executing schema-bound SQL queries
76
   */
77
  fetch(): SchemaSqlForgeSql;
78

79
  /**
80
   * Provides query analysis capabilities including EXPLAIN ANALYZE and slow query analysis.
81
   * @returns {SchemaAnalyzeForgeSql} Interface for analyzing query performance
82
   */
83
  analyze(): SchemaAnalyzeForgeSql;
84

85
  /**
86
   * Provides schema-level SQL operations with optimistic locking/versioning and automatic cache eviction.
87
   *
88
   * This method returns operations that use `modifyWithVersioning()` internally, providing:
89
   * - Optimistic locking support
90
   * - Automatic version field management
91
   * - Cache eviction after successful operations
92
   *
93
   * @returns {ForgeSQLCacheOperations} Interface for executing versioned SQL operations with cache management
94
   */
95
  modifyWithVersioningAndEvictCache(): ForgeSQLCacheOperations;
96
}
97

98
/**
99
 * Interface for Query Builder operations.
100
 * Provides access to the underlying Drizzle ORM query builder with enhanced functionality.
101
 *
102
 * This interface extends Drizzle's query building capabilities with:
103
 * - Field aliasing to prevent name collisions in joins
104
 * - Caching support for select operations
105
 * - Automatic cache eviction for modify operations
106
 *
107
 * @interface QueryBuilderForgeSql
108
 */
109
export interface QueryBuilderForgeSql {
110
  /**
111
   * Creates a new query builder for the given entity.
112
   * @returns {MySqlRemoteDatabase<Record<string, unknown>>} The Drizzle database instance for building queries
113
   */
114
  getDrizzleQueryBuilder(): MySqlRemoteDatabase<Record<string, unknown>> & {
115
    selectAliased: SelectAliasedType;
116
    selectAliasedDistinct: SelectAliasedDistinctType;
117
    selectAliasedCacheable: SelectAliasedCacheableType;
118
    selectAliasedDistinctCacheable: SelectAliasedDistinctCacheableType;
119
    insertWithCacheContext: InsertAndEvictCacheType;
120
    insertAndEvictCache: InsertAndEvictCacheType;
121
    updateAndEvictCache: UpdateAndEvictCacheType;
122
    updateWithCacheContext: UpdateAndEvictCacheType;
123
    deleteAndEvictCache: DeleteAndEvictCacheType;
124
    deleteWithCacheContext: DeleteAndEvictCacheType;
125
  };
126

127
  /**
128
   * Creates a select query with unique field aliases to prevent field name collisions in joins.
129
   * This is particularly useful when working with Atlassian Forge SQL, which collapses fields with the same name in joined tables.
130
   *
131
   * @template TSelection - The type of the selected fields
132
   * @param {TSelection} fields - Object containing the fields to select, with table schemas as values
133
   * @returns {MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>} A select query builder with unique field aliases
134
   * @throws {Error} If fields parameter is empty
135
   * @example
136
   * ```typescript
137
   * await forgeSQL
138
   *   .select({user: users, order: orders})
139
   *   .from(orders)
140
   *   .innerJoin(users, eq(orders.userId, users.id));
141
   * ```
142
   */
143
  select<TSelection extends SelectedFields>(
144
    fields: TSelection,
145
  ): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
146

147
  /**
148
   * Creates a distinct select query with unique field aliases to prevent field name collisions in joins.
149
   * This is particularly useful when working with Atlassian Forge SQL, which collapses fields with the same name in joined tables.
150
   *
151
   * @template TSelection - The type of the selected fields
152
   * @param {TSelection} fields - Object containing the fields to select, with table schemas as values
153
   * @returns {MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>} A distinct select query builder with unique field aliases
154
   * @throws {Error} If fields parameter is empty
155
   * @example
156
   * ```typescript
157
   * await forgeSQL
158
   *   .selectDistinct({user: users, order: orders})
159
   *   .from(orders)
160
   *   .innerJoin(users, eq(orders.userId, users.id));
161
   * ```
162
   */
163
  selectDistinct<TSelection extends SelectedFields>(
164
    fields: TSelection,
165
  ): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
166

167
  /**
168
   * Creates a cacheable select query with unique field aliases to prevent field name collisions in joins.
169
   * This is particularly useful when working with Atlassian Forge SQL, which collapses fields with the same name in joined tables.
170
   *
171
   * @template TSelection - The type of the selected fields
172
   * @param {TSelection} fields - Object containing the fields to select, with table schemas as values
173
   * @param {number} cacheTTL - cache ttl optional default is 60 sec.
174
   * @returns {MySqlSelectBuilder<TSelection, MySql2PreparedQueryHKT>} A select query builder with unique field aliases
175
   * @throws {Error} If fields parameter is empty
176
   * @example
177
   * ```typescript
178
   * await forgeSQL
179
   *   .selectCacheable({user: users, order: orders},60)
180
   *   .from(orders)
181
   *   .innerJoin(users, eq(orders.userId, users.id));
182
   * ```
183
   */
184
  selectCacheable<TSelection extends SelectedFields>(
185
    fields: TSelection,
186
    cacheTTL?: number,
187
  ): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
188

189
  /**
190
   * Creates a cacheable distinct select query with unique field aliases to prevent field name collisions in joins.
191
   * This is particularly useful when working with Atlassian Forge SQL, which collapses fields with the same name in joined tables.
192
   *
193
   * @template TSelection - The type of the selected fields
194
   * @param {TSelection} fields - Object containing the fields to select, with table schemas as values
195
   * @param {number} cacheTTL - cache ttl optional default is 60 sec.
196
   * @returns {MySqlSelectBuilder<TSelection, MySql2PreparedQueryHKT>} A distinct select query builder with unique field aliases
197
   * @throws {Error} If fields parameter is empty
198
   * @example
199
   * ```typescript
200
   * await forgeSQL
201
   *   .selectDistinctCacheable({user: users, order: orders}, 60)
202
   *   .from(orders)
203
   *   .innerJoin(users, eq(orders.userId, users.id));
204
   * ```
205
   */
206
  selectDistinctCacheable<TSelection extends SelectedFields>(
207
    fields: TSelection,
208
    cacheTTL?: number,
209
  ): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
210

211
  /**
212
   * Creates an insert query builder.
213
   *
214
   * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
215
   * For versioned inserts, use `modifyWithVersioning().insert()` or `modifyWithVersioningAndEvictCache().insert()` instead.
216
   *
217
   * @param table - The table to insert into
218
   * @returns Insert query builder (no versioning, no cache management)
219
   */
220
  insert<TTable extends MySqlTable>(
221
    table: TTable,
222
  ): MySqlInsertBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT>;
223

224
  /**
225
   * Creates an insert query builder that automatically evicts cache after execution.
226
   *
227
   * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
228
   * For versioned inserts, use `modifyWithVersioning().insert()` or `modifyWithVersioningAndEvictCache().insert()` instead.
229
   *
230
   * @param table - The table to insert into
231
   * @returns Insert query builder with automatic cache eviction (no versioning)
232
   */
233
  insertAndEvictCache<TTable extends MySqlTable>(
234
    table: TTable,
235
  ): MySqlInsertBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT>;
236

237
  /**
238
   * Creates an update query builder.
239
   *
240
   * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
241
   * For versioned updates, use `modifyWithVersioning().updateById()` or `modifyWithVersioningAndEvictCache().updateById()` instead.
242
   *
243
   * @param table - The table to update
244
   * @returns Update query builder (no versioning, no cache management)
245
   */
246
  update<TTable extends MySqlTable>(
247
    table: TTable,
248
  ): MySqlUpdateBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT>;
249

250
  /**
251
   * Creates an update query builder that automatically evicts cache after execution.
252
   *
253
   * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
254
   * For versioned updates, use `modifyWithVersioning().updateById()` or `modifyWithVersioningAndEvictCache().updateById()` instead.
255
   *
256
   * @param table - The table to update
257
   * @returns Update query builder with automatic cache eviction (no versioning)
258
   */
259
  updateAndEvictCache<TTable extends MySqlTable>(
260
    table: TTable,
261
  ): MySqlUpdateBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT>;
262

263
  /**
264
   * Creates a delete query builder.
265
   *
266
   * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
267
   * For versioned deletes, use `modifyWithVersioning().deleteById()` or `modifyWithVersioningAndEvictCache().deleteById()` instead.
268
   *
269
   * @param table - The table to delete from
270
   * @returns Delete query builder (no versioning, no cache management)
271
   */
272
  delete<TTable extends MySqlTable>(
273
    table: TTable,
274
  ): MySqlDeleteBase<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT>;
275
  /**
276
   * Creates a delete query builder that automatically evicts cache after execution.
277
   *
278
   * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
279
   * For versioned deletes, use `modifyWithVersioning().deleteById()` or `modifyWithVersioningAndEvictCache().deleteById()` instead.
280
   *
281
   * @param table - The table to delete from
282
   * @returns Delete query builder with automatic cache eviction (no versioning)
283
   */
284
  deleteAndEvictCache<TTable extends MySqlTable>(
285
    table: TTable,
286
  ): MySqlDeleteBase<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT>;
287

288
  /**
289
   * Executes operations within a cache context that collects cache eviction events.
290
   * All clearCache calls within the context are collected and executed in batch at the end.
291
   * Queries executed within this context will bypass cache for tables that were marked for clearing.
292
   *
293
   * @param cacheContext - Function containing operations that may trigger cache evictions
294
   * @returns Promise that resolves when all operations and cache clearing are complete
295
   */
296
  executeWithCacheContext(cacheContext: () => Promise<void>): Promise<void>;
297

298
  /**
299
   * Executes operations within a cache context and returns a value.
300
   * All clearCache calls within the context are collected and executed in batch at the end.
301
   * Queries executed within this context will bypass cache for tables that were marked for clearing.
302
   *
303
   * @param cacheContext - Function containing operations that may trigger cache evictions
304
   * @returns Promise that resolves to the return value of the cacheContext function
305
   */
306
  executeWithCacheContextAndReturnValue<T>(cacheContext: () => Promise<T>): Promise<T>;
307
}
308

309
/**
310
 * Interface for Modify (Create, Update, Delete) operations.
311
 * Provides methods for basic database operations with support for optimistic locking.
312
 *
313
 * @interface VerioningModificationForgeSQL
314
 */
315
export interface VerioningModificationForgeSQL {
316
  /**
317
   * Inserts multiple records into the database.
318
   * @template T - The type of the table schema
319
   * @param {T} schema - The entity schema
320
   * @param {InferInsertModel<T>[]} models - The list of entities to insert
321
   * @param {boolean} [updateIfExists] - Whether to update the row if it already exists (default: false)
322
   * @returns {Promise<number>} The number of inserted rows
323
   * @throws {Error} If the insert operation fails
324
   */
325
  insert<T extends AnyMySqlTable>(
326
    schema: T,
327
    models: InferInsertModel<T>[],
328
    updateIfExists?: boolean,
329
  ): Promise<number>;
330

331
  /**
332
   * Deletes a record by its ID.
333
   * @template T - The type of the table schema
334
   * @param {unknown} id - The ID of the record to delete
335
   * @param {T} schema - The entity schema
336
   * @returns {Promise<number>} The number of rows affected
337
   * @throws {Error} If the delete operation fails
338
   */
339
  deleteById<T extends AnyMySqlTable>(id: unknown, schema: T): Promise<number>;
340

341
  /**
342
   * Updates a record by its ID with optimistic locking support.
343
   * If a version field is defined in the schema, versioning is applied:
344
   * - the current record version is retrieved
345
   * - checked for concurrent modifications
346
   * - and then incremented
347
   *
348
   * @template T - The type of the table schema
349
   * @param {Partial<InferInsertModel<T>>} entity - The entity with updated values
350
   * @param {T} schema - The entity schema
351
   * @returns {Promise<number>} The number of rows affected
352
   * @throws {Error} If the primary key is not included in the update fields
353
   * @throws {Error} If optimistic locking check fails
354
   */
355
  updateById<T extends AnyMySqlTable>(
356
    entity: Partial<InferInsertModel<T>>,
357
    schema: T,
358
  ): Promise<number>;
359

360
  /**
361
   * Updates specified fields of records based on provided conditions.
362
   * If the "where" parameter is not provided, the WHERE clause is built from the entity fields
363
   * that are not included in the list of fields to update.
364
   *
365
   * @template T - The type of the table schema
366
   * @param {Partial<InferInsertModel<T>>} updateData - The object containing values to update
367
   * @param {T} schema - The entity schema
368
   * @param {SQL<unknown>} [where] - Optional filtering conditions for the WHERE clause
369
   * @returns {Promise<number>} The number of affected rows
370
   * @throws {Error} If no filtering criteria are provided
371
   * @throws {Error} If the update operation fails
372
   */
373
  updateFields<T extends AnyMySqlTable>(
374
    updateData: Partial<InferInsertModel<T>>,
375
    schema: T,
376
    where?: SQL<unknown>,
377
  ): Promise<number>;
378
}
379

380
export interface CacheForgeSQL extends VerioningModificationForgeSQL {
381
  evictCache(tables: string[]): Promise<void>;
382
  evictCacheEntities(tables: AnyMySqlTable[]): Promise<void>;
383
}
384

385
/**
386
 * Interface for schema analysis operations.
387
 * Provides methods for analyzing query performance and execution plans.
388
 *
389
 * @interface SchemaAnalyzeForgeSql
390
 */
391
export interface SchemaAnalyzeForgeSql {
392
  /**
393
   * Executes EXPLAIN on a Drizzle query.
394
   * @param {{ toSQL: () => Query }} query - The Drizzle query to analyze
395
   * @returns {Promise<ExplainAnalyzeRow[]>} The execution plan analysis results
396
   */
397
  explain(query: { toSQL: () => Query }): Promise<ExplainAnalyzeRow[]>;
398

399
  /**
400
   * Executes EXPLAIN on a raw SQL query.
401
   * @param {string} query - The SQL query to analyze
402
   * @param {unknown[]} bindParams - The query parameters
403
   * @returns {Promise<ExplainAnalyzeRow[]>} The execution plan analysis results
404
   */
405
  explainRaw(query: string, bindParams: unknown[]): Promise<ExplainAnalyzeRow[]>;
406

407
  /**
408
   * Executes EXPLAIN ANALYZE on a Drizzle query.
409
   * @param {{ toSQL: () => Query }} query - The Drizzle query to analyze
410
   * @returns {Promise<ExplainAnalyzeRow[]>} The execution plan analysis results
411
   */
412
  explainAnalyze(query: { toSQL: () => Query }): Promise<ExplainAnalyzeRow[]>;
413

414
  /**
415
   * Executes EXPLAIN ANALYZE on a raw SQL query.
416
   * @param {string} query - The SQL query to analyze
417
   * @param {unknown[]} bindParams - The query parameters
418
   * @returns {Promise<ExplainAnalyzeRow[]>} The execution plan analysis results
419
   */
420
  explainAnalyzeRaw(query: string, bindParams: unknown[]): Promise<ExplainAnalyzeRow[]>;
421

422
  /**
423
   * Analyzes slow queries from the database.
424
   * @returns {Promise<SlowQueryNormalized[]>} The normalized slow query data
425
   */
426
  analyzeSlowQueries(): Promise<SlowQueryNormalized[]>;
427

428
  /**
429
   * Analyzes query history for specific tables using Drizzle table objects.
430
   * @param {AnyMySqlTable[]} tables - The Drizzle table objects to analyze
431
   * @param {Date} [fromDate] - The start date for the analysis
432
   * @param {Date} [toDate] - The end date for the analysis
433
   * @returns {Promise<ClusterStatementRowCamelCase[]>} The analyzed query history
434
   */
435
  analyzeQueriesHistory(
436
    tables: AnyMySqlTable[],
437
    fromDate?: Date,
438
    toDate?: Date,
439
  ): Promise<ClusterStatementRowCamelCase[]>;
440

441
  /**
442
   * Analyzes query history for specific tables using raw table names.
443
   * @param {string[]} tables - The table names to analyze
444
   * @param {Date} [fromDate] - The start date for the analysis
445
   * @param {Date} [toDate] - The end date for the analysis
446
   * @returns {Promise<ClusterStatementRowCamelCase[]>} The analyzed query history
447
   */
448
  analyzeQueriesHistoryRaw(
449
    tables: string[],
450
    fromDate?: Date,
451
    toDate?: Date,
452
  ): Promise<ClusterStatementRowCamelCase[]>;
453
}
454

455
/**
456
 * Interface for schema-level SQL operations.
457
 * Provides methods for executing SQL queries with schema binding and type safety.
458
 *
459
 * @interface SchemaSqlForgeSql
460
 */
461
export interface SchemaSqlForgeSql {
462
  /**
463
   * Executes a Drizzle query and returns a single result.
464
   * @template T - The type of the query builder
465
   * @param {T} query - The Drizzle query to execute
466
   * @returns {Promise<Awaited<T> extends Array<any> ? Awaited<T>[number] | undefined : Awaited<T> | undefined>} A single result object or undefined
467
   * @throws {Error} If more than one record is returned
468
   * @throws {Error} If the query execution fails
469
   */
470
  executeQueryOnlyOne<T extends MySqlSelectDynamic<AnyMySqlSelectQueryBuilder>>(
471
    query: T,
472
  ): Promise<
473
    Awaited<T> extends Array<any> ? Awaited<T>[number] | undefined : Awaited<T> | undefined
474
  >;
475

476
  /**
477
   * Executes a raw SQL query and returns the results.
478
   * @template T - The type of the result objects
479
   * @param {string} query - The raw SQL query
480
   * @param {SqlParameters[]} [params] - Optional SQL parameters
481
   * @returns {Promise<T[]>} A list of results as objects
482
   * @throws {Error} If the query execution fails
483
   */
484
  executeRawSQL<T extends object | unknown>(query: string, params?: SqlParameters[]): Promise<T[]>;
485

486
  /**
487
   * Executes a raw SQL update query.
488
   *
489
   * @param query - The raw SQL update query
490
   * @param params - Optional SQL parameters
491
   * @returns Promise that resolves to the update response containing affected rows
492
   * @throws Error if the update operation fails
493
   */
494
  executeRawUpdateSQL(query: string, params?: unknown[]): Promise<UpdateQueryResponse>;
495
}
496

497
/**
498
 * Interface for version field metadata.
499
 * Defines the configuration for optimistic locking version fields.
500
 *
501
 * @interface VersionFieldMetadata
502
 */
503
export interface VersionFieldMetadata {
504
  /** Name of the version field */
505
  fieldName: string;
506
}
507

508
/**
509
 * Interface for table metadata.
510
 * Defines the configuration for a specific table.
511
 *
512
 * @interface TableMetadata
513
 */
514
export interface TableMetadata {
515
  /** Name of the table */
516
  tableName: string;
517
  /** Version field configuration for optimistic locking */
518
  versionField: VersionFieldMetadata;
519
}
520

521
/**
522
 * Type for additional metadata configuration.
523
 * Maps table names to their metadata configuration.
524
 *
525
 * @type {AdditionalMetadata}
526
 */
527
export type AdditionalMetadata = Record<string, TableMetadata>;
528

529
/**
530
 * Interface for ForgeSQL ORM options
531
 *
532
 * @interface ForgeSqlOrmOptions
533
 */
534
export interface ForgeSqlOrmOptions {
535
  /** Whether to log raw SQL queries */
536
  logRawSqlQuery?: boolean;
537
  /** Whether to disable optimistic locking */
538
  disableOptimisticLocking?: boolean;
539
  /** SQL hints to be applied to queries */
540
  hints?: SqlHints;
541
  cacheTTL?: number;
542
  cacheEntityName?: string;
543
  cacheEntityQueryName?: string;
544
  cacheWrapTable?: boolean;
545
  cacheEntityExpirationName?: string;
546
  cacheEntityDataName?: string;
547

548
  /**
549
   * Additional metadata for table configuration.
550
   * Allows specifying table-specific settings and behaviors.
551
   * @example
552
   * ```typescript
553
   * {
554
   *   users: {
555
   *     tableName: "users",
556
   *     versionField: {
557
   *       fieldName: "updatedAt",
558
   *       type: "datetime",
559
   *       nullable: false
560
   *     }
561
   *   }
562
   * }
563
   * ```
564
   */
565
  additionalMetadata?: AdditionalMetadata;
566
}
567

568
/**
569
 * Custom type for MySQL datetime fields.
570
 * Handles conversion between JavaScript Date objects and MySQL datetime strings.
571
 *
572
 * @type {CustomType}
573
 */
574
export const forgeDateTimeString = customType<{
1✔
575
  data: Date;
576
  driver: string;
577
  config: { format?: string };
578
}>({
1✔
579
  dataType() {
1✔
580
    return "datetime";
3✔
581
  },
3✔
582
  toDriver(value: Date) {
1✔
583
    return formatDateTime(value, "yyyy-LL-dd' 'HH:mm:ss.SSS");
8✔
584
  },
8✔
585
  fromDriver(value: unknown) {
1✔
586
    const format = "yyyy-LL-dd'T'HH:mm:ss.SSS";
7✔
587
    return parseDateTime(value as string, format);
7✔
588
  },
7✔
589
});
1✔
590

591
/**
592
 * Custom type for MySQL timestamp fields.
593
 * Handles conversion between JavaScript Date objects and MySQL timestamp strings.
594
 *
595
 * @type {CustomType}
596
 */
597
export const forgeTimestampString = customType<{
1✔
598
  data: Date;
599
  driver: string;
600
  config: { format?: string };
601
}>({
1✔
602
  dataType() {
1✔
603
    return "timestamp";
1✔
604
  },
1✔
605
  toDriver(value: Date) {
1✔
606
    return formatDateTime(value, "yyyy-LL-dd' 'HH:mm:ss.SSS");
2✔
607
  },
2✔
608
  fromDriver(value: unknown) {
1✔
609
    const format = "yyyy-LL-dd'T'HH:mm:ss.SSS";
×
610
    return parseDateTime(value as string, format);
×
611
  },
×
612
});
1✔
613

614
/**
615
 * Custom type for MySQL date fields.
616
 * Handles conversion between JavaScript Date objects and MySQL date strings.
617
 *
618
 * @type {CustomType}
619
 */
620
export const forgeDateString = customType<{
1✔
621
  data: Date;
622
  driver: string;
623
  config: { format?: string };
624
}>({
1✔
625
  dataType() {
1✔
626
    return "date";
×
627
  },
×
628
  toDriver(value: Date) {
1✔
NEW
629
    return formatDateTime(value, "yyyy-LL-dd");
×
630
  },
×
631
  fromDriver(value: unknown) {
1✔
632
    const format = "yyyy-LL-dd";
×
633
    return parseDateTime(value as string, format);
×
634
  },
×
635
});
1✔
636

637
/**
638
 * Custom type for MySQL time fields.
639
 * Handles conversion between JavaScript Date objects and MySQL time strings.
640
 *
641
 * @type {CustomType}
642
 */
643
export const forgeTimeString = customType<{
1✔
644
  data: Date;
645
  driver: string;
646
  config: { format?: string };
647
}>({
1✔
648
  dataType() {
1✔
649
    return "time";
×
650
  },
×
651
  toDriver(value: Date) {
1✔
NEW
652
    return formatDateTime(value, "HH:mm:ss.SSS");
×
653
  },
×
654
  fromDriver(value: unknown) {
1✔
655
    return parseDateTime(value as string, "HH:mm:ss.SSS");
×
656
  },
×
657
});
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc