forge-sql-orm 2.0.30 → 2.1.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 (46) hide show
  1. package/README.md +1090 -81
  2. package/dist/ForgeSQLORM.js +1080 -60
  3. package/dist/ForgeSQLORM.js.map +1 -1
  4. package/dist/ForgeSQLORM.mjs +1063 -60
  5. package/dist/ForgeSQLORM.mjs.map +1 -1
  6. package/dist/core/ForgeSQLAnalyseOperations.d.ts +1 -1
  7. package/dist/core/ForgeSQLAnalyseOperations.d.ts.map +1 -1
  8. package/dist/core/ForgeSQLCacheOperations.d.ts +119 -0
  9. package/dist/core/ForgeSQLCacheOperations.d.ts.map +1 -0
  10. package/dist/core/ForgeSQLCrudOperations.d.ts +38 -22
  11. package/dist/core/ForgeSQLCrudOperations.d.ts.map +1 -1
  12. package/dist/core/ForgeSQLORM.d.ts +104 -13
  13. package/dist/core/ForgeSQLORM.d.ts.map +1 -1
  14. package/dist/core/ForgeSQLQueryBuilder.d.ts +243 -15
  15. package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
  16. package/dist/index.d.ts +1 -1
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/lib/drizzle/extensions/additionalActions.d.ts +42 -0
  19. package/dist/lib/drizzle/extensions/additionalActions.d.ts.map +1 -0
  20. package/dist/utils/cacheContextUtils.d.ts +123 -0
  21. package/dist/utils/cacheContextUtils.d.ts.map +1 -0
  22. package/dist/utils/cacheUtils.d.ts +56 -0
  23. package/dist/utils/cacheUtils.d.ts.map +1 -0
  24. package/dist/utils/sqlUtils.d.ts +8 -0
  25. package/dist/utils/sqlUtils.d.ts.map +1 -1
  26. package/dist/webtriggers/clearCacheSchedulerTrigger.d.ts +46 -0
  27. package/dist/webtriggers/clearCacheSchedulerTrigger.d.ts.map +1 -0
  28. package/dist/webtriggers/index.d.ts +1 -0
  29. package/dist/webtriggers/index.d.ts.map +1 -1
  30. package/package.json +15 -12
  31. package/src/core/ForgeSQLAnalyseOperations.ts +1 -1
  32. package/src/core/ForgeSQLCacheOperations.ts +195 -0
  33. package/src/core/ForgeSQLCrudOperations.ts +49 -40
  34. package/src/core/ForgeSQLORM.ts +443 -34
  35. package/src/core/ForgeSQLQueryBuilder.ts +291 -20
  36. package/src/index.ts +1 -1
  37. package/src/lib/drizzle/extensions/additionalActions.ts +548 -0
  38. package/src/lib/drizzle/extensions/types.d.ts +68 -10
  39. package/src/utils/cacheContextUtils.ts +210 -0
  40. package/src/utils/cacheUtils.ts +403 -0
  41. package/src/utils/sqlUtils.ts +16 -0
  42. package/src/webtriggers/clearCacheSchedulerTrigger.ts +79 -0
  43. package/src/webtriggers/index.ts +1 -0
  44. package/dist/lib/drizzle/extensions/selectAliased.d.ts +0 -9
  45. package/dist/lib/drizzle/extensions/selectAliased.d.ts.map +0 -1
  46. package/src/lib/drizzle/extensions/selectAliased.ts +0 -72
@@ -1,18 +1,41 @@
1
1
  import { ForgeSQLCrudOperations } from "./ForgeSQLCrudOperations";
2
2
  import {
3
- CRUDForgeSQL,
3
+ VerioningModificationForgeSQL,
4
4
  ForgeSqlOperation,
5
5
  ForgeSqlOrmOptions,
6
6
  SchemaAnalyzeForgeSql,
7
7
  SchemaSqlForgeSql,
8
8
  } from "./ForgeSQLQueryBuilder";
9
9
  import { ForgeSQLSelectOperations } from "./ForgeSQLSelectOperations";
10
- import { drizzle, MySqlRemoteDatabase, MySqlRemotePreparedQueryHKT } from "drizzle-orm/mysql-proxy";
10
+ import {
11
+ drizzle,
12
+ MySqlRemoteDatabase,
13
+ MySqlRemotePreparedQueryHKT,
14
+ MySqlRemoteQueryResultHKT,
15
+ } from "drizzle-orm/mysql-proxy";
11
16
  import { createForgeDriverProxy } from "../utils/forgeDriverProxy";
12
17
  import type { SelectedFields } from "drizzle-orm/mysql-core/query-builders/select.types";
13
18
  import { MySqlSelectBuilder } from "drizzle-orm/mysql-core";
14
- import { patchDbWithSelectAliased } from "../lib/drizzle/extensions/selectAliased";
19
+ import {
20
+ DeleteAndEvictCacheType,
21
+ InsertAndEvictCacheType,
22
+ patchDbWithSelectAliased,
23
+ SelectAliasedCacheableType,
24
+ SelectAliasedDistinctCacheableType,
25
+ SelectAliasedDistinctType,
26
+ SelectAliasedType,
27
+ UpdateAndEvictCacheType,
28
+ } from "../lib/drizzle/extensions/additionalActions";
15
29
  import { ForgeSQLAnalyseOperation } from "./ForgeSQLAnalyseOperations";
30
+ import { ForgeSQLCacheOperations } from "./ForgeSQLCacheOperations";
31
+ import type { MySqlTable } from "drizzle-orm/mysql-core/table";
32
+ import {
33
+ MySqlDeleteBase,
34
+ MySqlInsertBuilder,
35
+ MySqlUpdateBuilder,
36
+ } from "drizzle-orm/mysql-core/query-builders";
37
+ import { cacheApplicationContext, localCacheApplicationContext } from "../utils/cacheContextUtils";
38
+ import { clearTablesCache } from "../utils/cacheUtils";
16
39
 
17
40
  /**
18
41
  * Implementation of ForgeSQLORM that uses Drizzle ORM for query building.
@@ -22,16 +45,22 @@ import { ForgeSQLAnalyseOperation } from "./ForgeSQLAnalyseOperations";
22
45
  class ForgeSQLORMImpl implements ForgeSqlOperation {
23
46
  private static instance: ForgeSQLORMImpl | null = null;
24
47
  private readonly drizzle: MySqlRemoteDatabase<any> & {
25
- selectAliased: <TSelection extends SelectedFields>(
26
- fields: TSelection,
27
- ) => MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
28
- selectAliasedDistinct: <TSelection extends SelectedFields>(
29
- fields: TSelection,
30
- ) => MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
48
+ selectAliased: SelectAliasedType;
49
+ selectAliasedDistinct: SelectAliasedDistinctType;
50
+ selectAliasedCacheable: SelectAliasedCacheableType;
51
+ selectAliasedDistinctCacheable: SelectAliasedDistinctCacheableType;
52
+ insertWithCacheContext: InsertAndEvictCacheType;
53
+ insertAndEvictCache: InsertAndEvictCacheType;
54
+ updateAndEvictCache: UpdateAndEvictCacheType;
55
+ updateWithCacheContext: UpdateAndEvictCacheType;
56
+ deleteAndEvictCache: DeleteAndEvictCacheType;
57
+ deleteWithCacheContext: DeleteAndEvictCacheType;
31
58
  };
32
- private readonly crudOperations: CRUDForgeSQL;
59
+ private readonly crudOperations: VerioningModificationForgeSQL;
33
60
  private readonly fetchOperations: SchemaSqlForgeSql;
34
61
  private readonly analyzeOperations: SchemaAnalyzeForgeSql;
62
+ private readonly cacheOperations: ForgeSQLCacheOperations;
63
+ private readonly options: ForgeSqlOrmOptions;
35
64
 
36
65
  /**
37
66
  * Private constructor to enforce singleton behavior.
@@ -42,7 +71,13 @@ class ForgeSQLORMImpl implements ForgeSqlOperation {
42
71
  const newOptions: ForgeSqlOrmOptions = options ?? {
43
72
  logRawSqlQuery: false,
44
73
  disableOptimisticLocking: false,
74
+ cacheWrapTable: true,
75
+ cacheTTL: 120,
76
+ cacheEntityQueryName: "sql",
77
+ cacheEntityExpirationName: "expiration",
78
+ cacheEntityDataName: "data",
45
79
  };
80
+ this.options = newOptions;
46
81
  if (newOptions.logRawSqlQuery) {
47
82
  console.debug("Initializing ForgeSQLORM...");
48
83
  }
@@ -50,21 +85,191 @@ class ForgeSQLORMImpl implements ForgeSqlOperation {
50
85
  const proxiedDriver = createForgeDriverProxy(newOptions.hints, newOptions.logRawSqlQuery);
51
86
  this.drizzle = patchDbWithSelectAliased(
52
87
  drizzle(proxiedDriver, { logger: newOptions.logRawSqlQuery }),
88
+ newOptions,
53
89
  );
54
90
  this.crudOperations = new ForgeSQLCrudOperations(this, newOptions);
55
91
  this.fetchOperations = new ForgeSQLSelectOperations(newOptions);
56
92
  this.analyzeOperations = new ForgeSQLAnalyseOperation(this);
93
+ this.cacheOperations = new ForgeSQLCacheOperations(newOptions, this);
57
94
  } catch (error) {
58
95
  console.error("ForgeSQLORM initialization failed:", error);
59
96
  throw error;
60
97
  }
61
98
  }
62
99
 
100
+ /**
101
+ * Executes operations within a cache context that collects cache eviction events.
102
+ * All clearCache calls within the context are collected and executed in batch at the end.
103
+ * Queries executed within this context will bypass cache for tables that were marked for clearing.
104
+ *
105
+ * This is useful for:
106
+ * - Batch operations that affect multiple tables
107
+ * - Transaction-like operations where you want to clear cache only at the end
108
+ * - Performance optimization by reducing cache clear operations
109
+ *
110
+ * @param cacheContext - Function containing operations that may trigger cache evictions
111
+ * @returns Promise that resolves when all operations and cache clearing are complete
112
+ *
113
+ * @example
114
+ * ```typescript
115
+ * await forgeSQL.executeWithCacheContext(async () => {
116
+ * await forgeSQL.modifyWithVersioning().insert(users, userData);
117
+ * await forgeSQL.modifyWithVersioning().insert(orders, orderData);
118
+ * // Cache for both users and orders tables will be cleared at the end
119
+ * });
120
+ * ```
121
+ */
122
+ executeWithCacheContext(cacheContext: () => Promise<void>): Promise<void> {
123
+ return this.executeWithCacheContextAndReturnValue<void>(cacheContext);
124
+ }
125
+
126
+ /**
127
+ * Executes operations within a cache context and returns a value.
128
+ * All clearCache calls within the context are collected and executed in batch at the end.
129
+ * Queries executed within this context will bypass cache for tables that were marked for clearing.
130
+ *
131
+ * @param cacheContext - Function containing operations that may trigger cache evictions
132
+ * @returns Promise that resolves to the return value of the cacheContext function
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * const result = await forgeSQL.executeWithCacheContextAndReturnValue(async () => {
137
+ * await forgeSQL.modifyWithVersioning().insert(users, userData);
138
+ * return await forgeSQL.fetch().executeQueryOnlyOne(selectUserQuery);
139
+ * });
140
+ * ```
141
+ */
142
+ async executeWithCacheContextAndReturnValue<T>(cacheContext: () => Promise<T>): Promise<T> {
143
+ return await this.executeWithLocalCacheContextAndReturnValue(
144
+ async () =>
145
+ await cacheApplicationContext.run({ tables: new Set<string>() }, async () => {
146
+ try {
147
+ return await cacheContext();
148
+ } finally {
149
+ await clearTablesCache(
150
+ Array.from(cacheApplicationContext.getStore()?.tables ?? []),
151
+ this.options,
152
+ );
153
+ }
154
+ }),
155
+ );
156
+ }
157
+ /**
158
+ * Executes operations within a local cache context and returns a value.
159
+ * This provides in-memory caching for select queries within a single request scope.
160
+ *
161
+ * @param cacheContext - Function containing operations that will benefit from local caching
162
+ * @returns Promise that resolves to the return value of the cacheContext function
163
+ */
164
+ async executeWithLocalCacheContextAndReturnValue<T>(cacheContext: () => Promise<T>): Promise<T> {
165
+ return await localCacheApplicationContext.run({ cache: {} }, async () => {
166
+ return await cacheContext();
167
+ });
168
+ }
169
+
170
+ /**
171
+ * Executes operations within a local cache context.
172
+ * This provides in-memory caching for select queries within a single request scope.
173
+ *
174
+ * @param cacheContext - Function containing operations that will benefit from local caching
175
+ * @returns Promise that resolves when all operations are complete
176
+ */
177
+ executeWithLocalContext(cacheContext: () => Promise<void>): Promise<void> {
178
+ return this.executeWithLocalCacheContextAndReturnValue<void>(cacheContext);
179
+ }
180
+ /**
181
+ * Creates an insert query builder.
182
+ *
183
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
184
+ * For versioned inserts, use `modifyWithVersioning().insert()` or `modifyWithVersioningAndEvictCache().insert()` instead.
185
+ *
186
+ * @param table - The table to insert into
187
+ * @returns Insert query builder (no versioning, no cache management)
188
+ */
189
+ insert<TTable extends MySqlTable>(
190
+ table: TTable,
191
+ ): MySqlInsertBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
192
+ return this.drizzle.insertWithCacheContext(table);
193
+ }
194
+ /**
195
+ * Creates an insert query builder that automatically evicts cache after execution.
196
+ *
197
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
198
+ * For versioned inserts, use `modifyWithVersioning().insert()` or `modifyWithVersioningAndEvictCache().insert()` instead.
199
+ *
200
+ * @param table - The table to insert into
201
+ * @returns Insert query builder with automatic cache eviction (no versioning)
202
+ */
203
+ insertAndEvictCache<TTable extends MySqlTable>(
204
+ table: TTable,
205
+ ): MySqlInsertBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
206
+ return this.drizzle.insertAndEvictCache(table);
207
+ }
208
+
209
+ /**
210
+ * Creates an update query builder that automatically evicts cache after execution.
211
+ *
212
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
213
+ * For versioned updates, use `modifyWithVersioning().updateById()` or `modifyWithVersioningAndEvictCache().updateById()` instead.
214
+ *
215
+ * @param table - The table to update
216
+ * @returns Update query builder with automatic cache eviction (no versioning)
217
+ */
218
+ updateAndEvictCache<TTable extends MySqlTable>(
219
+ table: TTable,
220
+ ): MySqlUpdateBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
221
+ return this.drizzle.updateAndEvictCache(table);
222
+ }
223
+
224
+ /**
225
+ * Creates an update query builder.
226
+ *
227
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
228
+ * For versioned updates, use `modifyWithVersioning().updateById()` or `modifyWithVersioningAndEvictCache().updateById()` instead.
229
+ *
230
+ * @param table - The table to update
231
+ * @returns Update query builder (no versioning, no cache management)
232
+ */
233
+ update<TTable extends MySqlTable>(
234
+ table: TTable,
235
+ ): MySqlUpdateBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
236
+ return this.drizzle.updateWithCacheContext(table);
237
+ }
238
+
239
+ /**
240
+ * Creates a delete query builder.
241
+ *
242
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
243
+ * For versioned deletes, use `modifyWithVersioning().deleteById()` or `modifyWithVersioningAndEvictCache().deleteById()` instead.
244
+ *
245
+ * @param table - The table to delete from
246
+ * @returns Delete query builder (no versioning, no cache management)
247
+ */
248
+ delete<TTable extends MySqlTable>(
249
+ table: TTable,
250
+ ): MySqlDeleteBase<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
251
+ return this.drizzle.deleteWithCacheContext(table);
252
+ }
253
+ /**
254
+ * Creates a delete query builder that automatically evicts cache after execution.
255
+ *
256
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
257
+ * For versioned deletes, use `modifyWithVersioning().deleteById()` or `modifyWithVersioningAndEvictCache().deleteById()` instead.
258
+ *
259
+ * @param table - The table to delete from
260
+ * @returns Delete query builder with automatic cache eviction (no versioning)
261
+ */
262
+ deleteAndEvictCache<TTable extends MySqlTable>(
263
+ table: TTable,
264
+ ): MySqlDeleteBase<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
265
+ return this.drizzle.deleteAndEvictCache(table);
266
+ }
267
+
63
268
  /**
64
269
  * Create the modify operations instance.
65
270
  * @returns modify operations.
66
271
  */
67
- modify(): CRUDForgeSQL {
272
+ modifyWithVersioning(): VerioningModificationForgeSQL {
68
273
  return this.crudOperations;
69
274
  }
70
275
 
@@ -78,14 +283,6 @@ class ForgeSQLORMImpl implements ForgeSqlOperation {
78
283
  return ForgeSQLORMImpl.instance;
79
284
  }
80
285
 
81
- /**
82
- * Retrieves the CRUD operations instance.
83
- * @returns CRUD operations.
84
- */
85
- crud(): CRUDForgeSQL {
86
- return this.modify();
87
- }
88
-
89
286
  /**
90
287
  * Retrieves the fetch operations instance.
91
288
  * @returns Fetch operations.
@@ -93,10 +290,29 @@ class ForgeSQLORMImpl implements ForgeSqlOperation {
93
290
  fetch(): SchemaSqlForgeSql {
94
291
  return this.fetchOperations;
95
292
  }
293
+
294
+ /**
295
+ * Provides query analysis capabilities including EXPLAIN ANALYZE and slow query analysis.
296
+ * @returns {SchemaAnalyzeForgeSql} Interface for analyzing query performance
297
+ */
96
298
  analyze(): SchemaAnalyzeForgeSql {
97
299
  return this.analyzeOperations;
98
300
  }
99
301
 
302
+ /**
303
+ * Provides schema-level SQL operations with optimistic locking/versioning and automatic cache eviction.
304
+ *
305
+ * This method returns operations that use `modifyWithVersioning()` internally, providing:
306
+ * - Optimistic locking support
307
+ * - Automatic version field management
308
+ * - Cache eviction after successful operations
309
+ *
310
+ * @returns {ForgeSQLCacheOperations} Interface for executing versioned SQL operations with cache management
311
+ */
312
+ modifyWithVersioningAndEvictCache(): ForgeSQLCacheOperations {
313
+ return this.cacheOperations;
314
+ }
315
+
100
316
  /**
101
317
  * Returns a Drizzle query builder instance.
102
318
  *
@@ -106,7 +322,18 @@ class ForgeSQLORMImpl implements ForgeSqlOperation {
106
322
  *
107
323
  * @returns A Drizzle query builder instance for query construction only.
108
324
  */
109
- getDrizzleQueryBuilder(): MySqlRemoteDatabase<Record<string, unknown>> {
325
+ getDrizzleQueryBuilder(): MySqlRemoteDatabase<Record<string, unknown>> & {
326
+ selectAliased: SelectAliasedType;
327
+ selectAliasedDistinct: SelectAliasedDistinctType;
328
+ selectAliasedCacheable: SelectAliasedCacheableType;
329
+ selectAliasedDistinctCacheable: SelectAliasedDistinctCacheableType;
330
+ insertWithCacheContext: InsertAndEvictCacheType;
331
+ insertAndEvictCache: InsertAndEvictCacheType;
332
+ updateAndEvictCache: UpdateAndEvictCacheType;
333
+ updateWithCacheContext: UpdateAndEvictCacheType;
334
+ deleteAndEvictCache: DeleteAndEvictCacheType;
335
+ deleteWithCacheContext: DeleteAndEvictCacheType;
336
+ } {
110
337
  return this.drizzle;
111
338
  }
112
339
 
@@ -159,6 +386,60 @@ class ForgeSQLORMImpl implements ForgeSqlOperation {
159
386
  }
160
387
  return this.drizzle.selectAliasedDistinct(fields);
161
388
  }
389
+
390
+ /**
391
+ * Creates a cacheable select query with unique field aliases to prevent field name collisions in joins.
392
+ * This is particularly useful when working with Atlassian Forge SQL, which collapses fields with the same name in joined tables.
393
+ *
394
+ * @template TSelection - The type of the selected fields
395
+ * @param {TSelection} fields - Object containing the fields to select, with table schemas as values
396
+ * @param {number} cacheTTL - cache ttl optional default is 60 sec.
397
+ * @returns {MySqlSelectBuilder<TSelection, MySql2PreparedQueryHKT>} A select query builder with unique field aliases
398
+ * @throws {Error} If fields parameter is empty
399
+ * @example
400
+ * ```typescript
401
+ * await forgeSQL
402
+ * .selectCacheable({user: users, order: orders},60)
403
+ * .from(orders)
404
+ * .innerJoin(users, eq(orders.userId, users.id));
405
+ * ```
406
+ */
407
+ selectCacheable<TSelection extends SelectedFields>(
408
+ fields: TSelection,
409
+ cacheTTL?: number,
410
+ ): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT> {
411
+ if (!fields) {
412
+ throw new Error("fields is empty");
413
+ }
414
+ return this.drizzle.selectAliasedCacheable(fields, cacheTTL);
415
+ }
416
+
417
+ /**
418
+ * Creates a cacheable distinct select query with unique field aliases to prevent field name collisions in joins.
419
+ * This is particularly useful when working with Atlassian Forge SQL, which collapses fields with the same name in joined tables.
420
+ *
421
+ * @template TSelection - The type of the selected fields
422
+ * @param {TSelection} fields - Object containing the fields to select, with table schemas as values
423
+ * @param {number} cacheTTL - cache ttl optional default is 60 sec.
424
+ * @returns {MySqlSelectBuilder<TSelection, MySql2PreparedQueryHKT>} A distinct select query builder with unique field aliases
425
+ * @throws {Error} If fields parameter is empty
426
+ * @example
427
+ * ```typescript
428
+ * await forgeSQL
429
+ * .selectDistinctCacheable({user: users, order: orders}, 60)
430
+ * .from(orders)
431
+ * .innerJoin(users, eq(orders.userId, users.id));
432
+ * ```
433
+ */
434
+ selectDistinctCacheable<TSelection extends SelectedFields>(
435
+ fields: TSelection,
436
+ cacheTTL?: number,
437
+ ): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT> {
438
+ if (!fields) {
439
+ throw new Error("fields is empty");
440
+ }
441
+ return this.drizzle.selectAliasedDistinctCacheable(fields, cacheTTL);
442
+ }
162
443
  }
163
444
 
164
445
  /**
@@ -172,6 +453,138 @@ class ForgeSQLORM implements ForgeSqlOperation {
172
453
  this.ormInstance = ForgeSQLORMImpl.getInstance(options);
173
454
  }
174
455
 
456
+ selectCacheable<TSelection extends SelectedFields>(
457
+ fields: TSelection,
458
+ cacheTTL?: number,
459
+ ): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT> {
460
+ return this.ormInstance.selectCacheable(fields, cacheTTL);
461
+ }
462
+
463
+ selectDistinctCacheable<TSelection extends SelectedFields>(
464
+ fields: TSelection,
465
+ cacheTTL?: number,
466
+ ): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT> {
467
+ return this.ormInstance.selectDistinctCacheable(fields, cacheTTL);
468
+ }
469
+
470
+ executeWithCacheContext(cacheContext: () => Promise<void>): Promise<void> {
471
+ return this.ormInstance.executeWithCacheContext(cacheContext);
472
+ }
473
+ executeWithCacheContextAndReturnValue<T>(cacheContext: () => Promise<T>): Promise<T> {
474
+ return this.ormInstance.executeWithCacheContextAndReturnValue(cacheContext);
475
+ }
476
+ /**
477
+ * Executes operations within a local cache context.
478
+ * This provides in-memory caching for select queries within a single request scope.
479
+ *
480
+ * @param cacheContext - Function containing operations that will benefit from local caching
481
+ * @returns Promise that resolves when all operations are complete
482
+ */
483
+ executeWithLocalContext(cacheContext: () => Promise<void>): Promise<void> {
484
+ return this.ormInstance.executeWithLocalContext(cacheContext);
485
+ }
486
+
487
+ /**
488
+ * Executes operations within a local cache context and returns a value.
489
+ * This provides in-memory caching for select queries within a single request scope.
490
+ *
491
+ * @param cacheContext - Function containing operations that will benefit from local caching
492
+ * @returns Promise that resolves to the return value of the cacheContext function
493
+ */
494
+ executeWithLocalCacheContextAndReturnValue<T>(cacheContext: () => Promise<T>): Promise<T> {
495
+ return this.ormInstance.executeWithLocalCacheContextAndReturnValue(cacheContext);
496
+ }
497
+
498
+ /**
499
+ * Creates an insert query builder.
500
+ *
501
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
502
+ * For versioned inserts, use `modifyWithVersioning().insert()` or `modifyWithVersioningAndEvictCache().insert()` instead.
503
+ *
504
+ * @param table - The table to insert into
505
+ * @returns Insert query builder (no versioning, no cache management)
506
+ */
507
+ insert<TTable extends MySqlTable>(
508
+ table: TTable,
509
+ ): MySqlInsertBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
510
+ return this.ormInstance.insert(table);
511
+ }
512
+
513
+ /**
514
+ * Creates an insert query builder that automatically evicts cache after execution.
515
+ *
516
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
517
+ * For versioned inserts, use `modifyWithVersioning().insert()` or `modifyWithVersioningAndEvictCache().insert()` instead.
518
+ *
519
+ * @param table - The table to insert into
520
+ * @returns Insert query builder with automatic cache eviction (no versioning)
521
+ */
522
+ insertAndEvictCache<TTable extends MySqlTable>(
523
+ table: TTable,
524
+ ): MySqlInsertBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
525
+ return this.ormInstance.insertAndEvictCache(table);
526
+ }
527
+
528
+ /**
529
+ * Creates an update query builder.
530
+ *
531
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
532
+ * For versioned updates, use `modifyWithVersioning().updateById()` or `modifyWithVersioningAndEvictCache().updateById()` instead.
533
+ *
534
+ * @param table - The table to update
535
+ * @returns Update query builder (no versioning, no cache management)
536
+ */
537
+ update<TTable extends MySqlTable>(
538
+ table: TTable,
539
+ ): MySqlUpdateBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
540
+ return this.ormInstance.update(table);
541
+ }
542
+
543
+ /**
544
+ * Creates an update query builder that automatically evicts cache after execution.
545
+ *
546
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
547
+ * For versioned updates, use `modifyWithVersioning().updateById()` or `modifyWithVersioningAndEvictCache().updateById()` instead.
548
+ *
549
+ * @param table - The table to update
550
+ * @returns Update query builder with automatic cache eviction (no versioning)
551
+ */
552
+ updateAndEvictCache<TTable extends MySqlTable>(
553
+ table: TTable,
554
+ ): MySqlUpdateBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
555
+ return this.ormInstance.updateAndEvictCache(table);
556
+ }
557
+
558
+ /**
559
+ * Creates a delete query builder.
560
+ *
561
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
562
+ * For versioned deletes, use `modifyWithVersioning().deleteById()` or `modifyWithVersioningAndEvictCache().deleteById()` instead.
563
+ *
564
+ * @param table - The table to delete from
565
+ * @returns Delete query builder (no versioning, no cache management)
566
+ */
567
+ delete<TTable extends MySqlTable>(
568
+ table: TTable,
569
+ ): MySqlDeleteBase<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
570
+ return this.ormInstance.delete(table);
571
+ }
572
+
573
+ /**
574
+ * Creates a delete query builder that automatically evicts cache after execution.
575
+ *
576
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
577
+ * For versioned deletes, use `modifyWithVersioning().deleteById()` or `modifyWithVersioningAndEvictCache().deleteById()` instead.
578
+ *
579
+ * @param table - The table to delete from
580
+ * @returns Delete query builder with automatic cache eviction (no versioning)
581
+ */
582
+ deleteAndEvictCache<TTable extends MySqlTable>(
583
+ table: TTable,
584
+ ): MySqlDeleteBase<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
585
+ return this.ormInstance.deleteAndEvictCache(table);
586
+ }
587
+
175
588
  /**
176
589
  * Creates a select query with unique field aliases to prevent field name collisions in joins.
177
590
  * This is particularly useful when working with Atlassian Forge SQL, which collapses fields with the same name in joined tables.
@@ -216,20 +629,12 @@ class ForgeSQLORM implements ForgeSqlOperation {
216
629
  return this.ormInstance.selectDistinct(fields);
217
630
  }
218
631
 
219
- /**
220
- * Proxies the `crud` method from `ForgeSQLORMImpl`.
221
- * @returns CRUD operations.
222
- */
223
- crud(): CRUDForgeSQL {
224
- return this.ormInstance.modify();
225
- }
226
-
227
632
  /**
228
633
  * Proxies the `modify` method from `ForgeSQLORMImpl`.
229
634
  * @returns Modify operations.
230
635
  */
231
- modify(): CRUDForgeSQL {
232
- return this.ormInstance.modify();
636
+ modifyWithVersioning(): VerioningModificationForgeSQL {
637
+ return this.ormInstance.modifyWithVersioning();
233
638
  }
234
639
 
235
640
  /**
@@ -248,13 +653,17 @@ class ForgeSQLORM implements ForgeSqlOperation {
248
653
  return this.ormInstance.analyze();
249
654
  }
250
655
 
656
+ /**
657
+ * Provides schema-level SQL cacheable operations with type safety.
658
+ * @returns {ForgeSQLCacheOperations} Interface for executing schema-bound SQL queries
659
+ */
660
+ modifyWithVersioningAndEvictCache(): ForgeSQLCacheOperations {
661
+ return this.ormInstance.modifyWithVersioningAndEvictCache();
662
+ }
663
+
251
664
  /**
252
665
  * Returns a Drizzle query builder instance.
253
666
  *
254
- * ⚠️ IMPORTANT: This method should be used ONLY for query building purposes.
255
- * The returned instance should NOT be used for direct database connections or query execution.
256
- * All database operations should be performed through Forge SQL's executeRawSQL or executeRawUpdateSQL methods.
257
- *
258
667
  * @returns A Drizzle query builder instance for query construction only.
259
668
  */
260
669
  getDrizzleQueryBuilder() {