forge-sql-orm 2.1.12 → 2.1.14

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 (79) hide show
  1. package/README.md +922 -549
  2. package/dist/core/ForgeSQLAnalyseOperations.d.ts.map +1 -1
  3. package/dist/core/ForgeSQLAnalyseOperations.js +257 -0
  4. package/dist/core/ForgeSQLAnalyseOperations.js.map +1 -0
  5. package/dist/core/ForgeSQLCacheOperations.js +172 -0
  6. package/dist/core/ForgeSQLCacheOperations.js.map +1 -0
  7. package/dist/core/ForgeSQLCrudOperations.js +349 -0
  8. package/dist/core/ForgeSQLCrudOperations.js.map +1 -0
  9. package/dist/core/ForgeSQLORM.d.ts +29 -1
  10. package/dist/core/ForgeSQLORM.d.ts.map +1 -1
  11. package/dist/core/ForgeSQLORM.js +1252 -0
  12. package/dist/core/ForgeSQLORM.js.map +1 -0
  13. package/dist/core/ForgeSQLQueryBuilder.d.ts +179 -1
  14. package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
  15. package/dist/core/ForgeSQLQueryBuilder.js +77 -0
  16. package/dist/core/ForgeSQLQueryBuilder.js.map +1 -0
  17. package/dist/core/ForgeSQLSelectOperations.js +81 -0
  18. package/dist/core/ForgeSQLSelectOperations.js.map +1 -0
  19. package/dist/core/Rovo.d.ts +116 -0
  20. package/dist/core/Rovo.d.ts.map +1 -0
  21. package/dist/core/Rovo.js +647 -0
  22. package/dist/core/Rovo.js.map +1 -0
  23. package/dist/core/SystemTables.js +258 -0
  24. package/dist/core/SystemTables.js.map +1 -0
  25. package/dist/index.js +30 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/lib/drizzle/extensions/additionalActions.d.ts.map +1 -1
  28. package/dist/lib/drizzle/extensions/additionalActions.js +527 -0
  29. package/dist/lib/drizzle/extensions/additionalActions.js.map +1 -0
  30. package/dist/utils/cacheContextUtils.d.ts.map +1 -1
  31. package/dist/utils/cacheContextUtils.js +198 -0
  32. package/dist/utils/cacheContextUtils.js.map +1 -0
  33. package/dist/utils/cacheUtils.d.ts.map +1 -1
  34. package/dist/utils/cacheUtils.js +383 -0
  35. package/dist/utils/cacheUtils.js.map +1 -0
  36. package/dist/utils/forgeDriver.d.ts.map +1 -1
  37. package/dist/utils/forgeDriver.js +139 -0
  38. package/dist/utils/forgeDriver.js.map +1 -0
  39. package/dist/utils/forgeDriverProxy.js +68 -0
  40. package/dist/utils/forgeDriverProxy.js.map +1 -0
  41. package/dist/utils/metadataContextUtils.d.ts.map +1 -1
  42. package/dist/utils/metadataContextUtils.js +26 -0
  43. package/dist/utils/metadataContextUtils.js.map +1 -0
  44. package/dist/utils/requestTypeContextUtils.js +10 -0
  45. package/dist/utils/requestTypeContextUtils.js.map +1 -0
  46. package/dist/utils/sqlHints.js +52 -0
  47. package/dist/utils/sqlHints.js.map +1 -0
  48. package/dist/utils/sqlUtils.d.ts.map +1 -1
  49. package/dist/utils/sqlUtils.js +590 -0
  50. package/dist/utils/sqlUtils.js.map +1 -0
  51. package/dist/webtriggers/applyMigrationsWebTrigger.js +77 -0
  52. package/dist/webtriggers/applyMigrationsWebTrigger.js.map +1 -0
  53. package/dist/webtriggers/clearCacheSchedulerTrigger.js +83 -0
  54. package/dist/webtriggers/clearCacheSchedulerTrigger.js.map +1 -0
  55. package/dist/webtriggers/dropMigrationWebTrigger.js +54 -0
  56. package/dist/webtriggers/dropMigrationWebTrigger.js.map +1 -0
  57. package/dist/webtriggers/dropTablesMigrationWebTrigger.js +54 -0
  58. package/dist/webtriggers/dropTablesMigrationWebTrigger.js.map +1 -0
  59. package/dist/webtriggers/fetchSchemaWebTrigger.js +82 -0
  60. package/dist/webtriggers/fetchSchemaWebTrigger.js.map +1 -0
  61. package/dist/webtriggers/index.js +40 -0
  62. package/dist/webtriggers/index.js.map +1 -0
  63. package/dist/webtriggers/slowQuerySchedulerTrigger.js +80 -0
  64. package/dist/webtriggers/slowQuerySchedulerTrigger.js.map +1 -0
  65. package/package.json +31 -25
  66. package/src/core/ForgeSQLAnalyseOperations.ts +3 -2
  67. package/src/core/ForgeSQLORM.ts +64 -0
  68. package/src/core/ForgeSQLQueryBuilder.ts +200 -1
  69. package/src/core/Rovo.ts +765 -0
  70. package/src/lib/drizzle/extensions/additionalActions.ts +11 -0
  71. package/src/utils/cacheContextUtils.ts +9 -6
  72. package/src/utils/cacheUtils.ts +6 -4
  73. package/src/utils/forgeDriver.ts +3 -7
  74. package/src/utils/metadataContextUtils.ts +1 -3
  75. package/src/utils/sqlUtils.ts +33 -34
  76. package/dist/ForgeSQLORM.js +0 -3922
  77. package/dist/ForgeSQLORM.js.map +0 -1
  78. package/dist/ForgeSQLORM.mjs +0 -3905
  79. package/dist/ForgeSQLORM.mjs.map +0 -1
@@ -0,0 +1,1252 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const ForgeSQLCrudOperations_1 = require("./ForgeSQLCrudOperations");
4
+ const ForgeSQLSelectOperations_1 = require("./ForgeSQLSelectOperations");
5
+ const mysql_proxy_1 = require("drizzle-orm/mysql-proxy");
6
+ const forgeDriverProxy_1 = require("../utils/forgeDriverProxy");
7
+ const additionalActions_1 = require("../lib/drizzle/extensions/additionalActions");
8
+ const ForgeSQLAnalyseOperations_1 = require("./ForgeSQLAnalyseOperations");
9
+ const ForgeSQLCacheOperations_1 = require("./ForgeSQLCacheOperations");
10
+ const cacheContextUtils_1 = require("../utils/cacheContextUtils");
11
+ const cacheUtils_1 = require("../utils/cacheUtils");
12
+ const metadataContextUtils_1 = require("../utils/metadataContextUtils");
13
+ const requestTypeContextUtils_1 = require("../utils/requestTypeContextUtils");
14
+ const Rovo_1 = require("./Rovo");
15
+ /**
16
+ * Implementation of ForgeSQLORM that uses Drizzle ORM for query building.
17
+ * This class provides a bridge between Forge SQL and Drizzle ORM, allowing
18
+ * to use Drizzle's query builder while executing queries through Forge SQL.
19
+ */
20
+ class ForgeSQLORMImpl {
21
+ static instance = null;
22
+ drizzle;
23
+ crudOperations;
24
+ fetchOperations;
25
+ analyzeOperations;
26
+ cacheOperations;
27
+ options;
28
+ /**
29
+ * Private constructor to enforce singleton behavior.
30
+ * @param options - Options for configuring ForgeSQL ORM behavior.
31
+ */
32
+ constructor(options) {
33
+ try {
34
+ const newOptions = options ?? {
35
+ logRawSqlQuery: false,
36
+ logCache: false,
37
+ disableOptimisticLocking: false,
38
+ cacheWrapTable: true,
39
+ cacheTTL: 120,
40
+ cacheEntityQueryName: "sql",
41
+ cacheEntityExpirationName: "expiration",
42
+ cacheEntityDataName: "data",
43
+ };
44
+ this.options = newOptions;
45
+ if (newOptions.logRawSqlQuery) {
46
+ // eslint-disable-next-line no-console
47
+ console.debug("Initializing ForgeSQLORM...");
48
+ }
49
+ // Initialize Drizzle instance with our custom driver
50
+ const proxiedDriver = (0, forgeDriverProxy_1.createForgeDriverProxy)(this, newOptions.hints, newOptions.logRawSqlQuery);
51
+ this.drizzle = (0, additionalActions_1.patchDbWithSelectAliased)((0, mysql_proxy_1.drizzle)(proxiedDriver, { logger: newOptions.logRawSqlQuery }), newOptions);
52
+ this.crudOperations = new ForgeSQLCrudOperations_1.ForgeSQLCrudOperations(this, newOptions);
53
+ this.fetchOperations = new ForgeSQLSelectOperations_1.ForgeSQLSelectOperations(newOptions);
54
+ this.analyzeOperations = new ForgeSQLAnalyseOperations_1.ForgeSQLAnalyseOperation(this);
55
+ this.cacheOperations = new ForgeSQLCacheOperations_1.ForgeSQLCacheOperations(newOptions, this);
56
+ }
57
+ catch (error) {
58
+ // eslint-disable-next-line no-console
59
+ console.error("ForgeSQLORM initialization failed:", error);
60
+ throw error;
61
+ }
62
+ }
63
+ /**
64
+ * Executes a query and provides access to execution metadata with performance monitoring.
65
+ * This method allows you to capture detailed information about query execution
66
+ * including database execution time, response size, and query analysis capabilities.
67
+ *
68
+ * The method aggregates metrics across all database operations within the query function,
69
+ * making it ideal for monitoring resolver performance and detecting performance issues.
70
+ *
71
+ * @template T - The return type of the query
72
+ * @param query - A function that returns a Promise with the query result. Can contain multiple database operations.
73
+ * @param onMetadata - Callback function that receives aggregated execution metadata
74
+ * @param onMetadata.totalDbExecutionTime - Total database execution time across all operations in the query function (in milliseconds)
75
+ * @param onMetadata.totalResponseSize - Total response size across all operations (in bytes)
76
+ * @param onMetadata.printQueries - Function to analyze and print query execution plans from CLUSTER_STATEMENTS_SUMMARY
77
+ * @returns Promise with the query result
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * // Basic usage with performance monitoring
82
+ * const result = await forgeSQL.executeWithMetadata(
83
+ * async () => {
84
+ * const users = await forgeSQL.selectFrom(usersTable);
85
+ * const orders = await forgeSQL.selectFrom(ordersTable).where(eq(ordersTable.userId, usersTable.id));
86
+ * return { users, orders };
87
+ * },
88
+ * (totalDbExecutionTime, totalResponseSize, printQueries) => {
89
+ * const threshold = 500; // ms baseline for this resolver
90
+ *
91
+ * if (totalDbExecutionTime > threshold * 1.5) {
92
+ * console.warn(`[Performance Warning] Resolver exceeded DB time: ${totalDbExecutionTime} ms`);
93
+ * await printQueries(); // Analyze and print query execution plans
94
+ * } else if (totalDbExecutionTime > threshold) {
95
+ * console.debug(`[Performance Debug] High DB time: ${totalDbExecutionTime} ms`);
96
+ * }
97
+ *
98
+ * console.log(`DB response size: ${totalResponseSize} bytes`);
99
+ * }
100
+ * );
101
+ * ```
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * // Resolver with performance monitoring
106
+ * resolver.define("fetch", async (req: Request) => {
107
+ * try {
108
+ * return await forgeSQL.executeWithMetadata(
109
+ * async () => {
110
+ * // Resolver logic with multiple queries
111
+ * const users = await forgeSQL.selectFrom(demoUsers);
112
+ * const orders = await forgeSQL.selectFrom(demoOrders)
113
+ * .where(eq(demoOrders.userId, demoUsers.id));
114
+ * return { users, orders };
115
+ * },
116
+ * async (totalDbExecutionTime, totalResponseSize, printQueries) => {
117
+ * const threshold = 500; // ms baseline for this resolver
118
+ *
119
+ * if (totalDbExecutionTime > threshold * 1.5) {
120
+ * console.warn(`[Performance Warning fetch] Resolver exceeded DB time: ${totalDbExecutionTime} ms`);
121
+ * await printQueries(); // Optionally log or capture diagnostics for further analysis
122
+ * } else if (totalDbExecutionTime > threshold) {
123
+ * console.debug(`[Performance Debug] High DB time: ${totalDbExecutionTime} ms`);
124
+ * }
125
+ *
126
+ * console.log(`DB response size: ${totalResponseSize} bytes`);
127
+ * }
128
+ * );
129
+ * } catch (e) {
130
+ * const error = e?.cause?.debug?.sqlMessage ?? e?.cause;
131
+ * console.error(error, e);
132
+ * throw error;
133
+ * }
134
+ * });
135
+ * ```
136
+ *
137
+ * @note **Important**: When multiple resolvers are running concurrently, their query data may also appear in `printQueries()` analysis, as it queries the global `CLUSTER_STATEMENTS_SUMMARY` table.
138
+ */
139
+ async executeWithMetadata(query, onMetadata) {
140
+ return metadataContextUtils_1.metadataQueryContext.run({
141
+ totalDbExecutionTime: 0,
142
+ totalResponseSize: 0,
143
+ beginTime: new Date(),
144
+ forgeSQLORM: this,
145
+ printQueriesWithPlan: async () => {
146
+ return;
147
+ },
148
+ }, async () => {
149
+ const result = await query();
150
+ const metadata = await (0, metadataContextUtils_1.getLastestMetadata)();
151
+ try {
152
+ if (metadata) {
153
+ await onMetadata(metadata.totalDbExecutionTime, metadata.totalResponseSize, metadata.printQueriesWithPlan);
154
+ }
155
+ }
156
+ catch (e) {
157
+ // eslint-disable-next-line no-console
158
+ console.error("[ForgeSQLORM][executeWithMetadata] Failed to run onMetadata callback", {
159
+ errorMessage: e?.message,
160
+ errorStack: e?.stack,
161
+ totalDbExecutionTime: metadata?.totalDbExecutionTime,
162
+ totalResponseSize: metadata?.totalResponseSize,
163
+ beginTime: metadata?.beginTime,
164
+ }, e);
165
+ }
166
+ return result;
167
+ });
168
+ }
169
+ /**
170
+ * Executes operations within a cache context that collects cache eviction events.
171
+ * All clearCache calls within the context are collected and executed in batch at the end.
172
+ * Queries executed within this context will bypass cache for tables that were marked for clearing.
173
+ *
174
+ * This is useful for:
175
+ * - Batch operations that affect multiple tables
176
+ * - Transaction-like operations where you want to clear cache only at the end
177
+ * - Performance optimization by reducing cache clear operations
178
+ *
179
+ * @param cacheContext - Function containing operations that may trigger cache evictions
180
+ * @returns Promise that resolves when all operations and cache clearing are complete
181
+ *
182
+ * @example
183
+ * ```typescript
184
+ * await forgeSQL.executeWithCacheContext(async () => {
185
+ * await forgeSQL.modifyWithVersioning().insert(users, userData);
186
+ * await forgeSQL.modifyWithVersioning().insert(orders, orderData);
187
+ * // Cache for both users and orders tables will be cleared at the end
188
+ * });
189
+ * ```
190
+ */
191
+ executeWithCacheContext(cacheContext) {
192
+ return this.executeWithCacheContextAndReturnValue(cacheContext);
193
+ }
194
+ /**
195
+ * Executes operations within a cache context and returns a value.
196
+ * All clearCache calls within the context are collected and executed in batch at the end.
197
+ * Queries executed within this context will bypass cache for tables that were marked for clearing.
198
+ *
199
+ * @param cacheContext - Function containing operations that may trigger cache evictions
200
+ * @returns Promise that resolves to the return value of the cacheContext function
201
+ *
202
+ * @example
203
+ * ```typescript
204
+ * const result = await forgeSQL.executeWithCacheContextAndReturnValue(async () => {
205
+ * await forgeSQL.modifyWithVersioning().insert(users, userData);
206
+ * return await forgeSQL.fetch().executeQueryOnlyOne(selectUserQuery);
207
+ * });
208
+ * ```
209
+ */
210
+ async executeWithCacheContextAndReturnValue(cacheContext) {
211
+ return await this.executeWithLocalCacheContextAndReturnValue(async () => await cacheContextUtils_1.cacheApplicationContext.run(cacheContextUtils_1.cacheApplicationContext.getStore() ?? { tables: new Set() }, async () => {
212
+ try {
213
+ return await cacheContext();
214
+ }
215
+ finally {
216
+ await (0, cacheUtils_1.clearTablesCache)(Array.from(cacheContextUtils_1.cacheApplicationContext.getStore()?.tables ?? []), this.options);
217
+ }
218
+ }));
219
+ }
220
+ /**
221
+ * Executes operations within a local cache context and returns a value.
222
+ * This provides in-memory caching for select queries within a single request scope.
223
+ *
224
+ * @param cacheContext - Function containing operations that will benefit from local caching
225
+ * @returns Promise that resolves to the return value of the cacheContext function
226
+ */
227
+ async executeWithLocalCacheContextAndReturnValue(cacheContext) {
228
+ return await cacheContextUtils_1.localCacheApplicationContext.run(cacheContextUtils_1.localCacheApplicationContext.getStore() ?? { cache: {} }, async () => {
229
+ return await cacheContext();
230
+ });
231
+ }
232
+ /**
233
+ * Executes operations within a local cache context.
234
+ * This provides in-memory caching for select queries within a single request scope.
235
+ *
236
+ * @param cacheContext - Function containing operations that will benefit from local caching
237
+ * @returns Promise that resolves when all operations are complete
238
+ */
239
+ executeWithLocalContext(cacheContext) {
240
+ return this.executeWithLocalCacheContextAndReturnValue(cacheContext);
241
+ }
242
+ /**
243
+ * Creates an insert query builder.
244
+ *
245
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
246
+ * For versioned inserts, use `modifyWithVersioning().insert()` or `modifyWithVersioningAndEvictCache().insert()` instead.
247
+ *
248
+ * @param table - The table to insert into
249
+ * @returns Insert query builder (no versioning, no cache management)
250
+ */
251
+ insert(table) {
252
+ return this.drizzle.insertWithCacheContext(table);
253
+ }
254
+ /**
255
+ * Creates an insert query builder that automatically evicts cache after execution.
256
+ *
257
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
258
+ * For versioned inserts, use `modifyWithVersioning().insert()` or `modifyWithVersioningAndEvictCache().insert()` instead.
259
+ *
260
+ * @param table - The table to insert into
261
+ * @returns Insert query builder with automatic cache eviction (no versioning)
262
+ */
263
+ insertAndEvictCache(table) {
264
+ return this.drizzle.insertAndEvictCache(table);
265
+ }
266
+ /**
267
+ * Creates an update query builder that automatically evicts cache after execution.
268
+ *
269
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
270
+ * For versioned updates, use `modifyWithVersioning().updateById()` or `modifyWithVersioningAndEvictCache().updateById()` instead.
271
+ *
272
+ * @param table - The table to update
273
+ * @returns Update query builder with automatic cache eviction (no versioning)
274
+ */
275
+ updateAndEvictCache(table) {
276
+ return this.drizzle.updateAndEvictCache(table);
277
+ }
278
+ /**
279
+ * Creates an update query builder.
280
+ *
281
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
282
+ * For versioned updates, use `modifyWithVersioning().updateById()` or `modifyWithVersioningAndEvictCache().updateById()` instead.
283
+ *
284
+ * @param table - The table to update
285
+ * @returns Update query builder (no versioning, no cache management)
286
+ */
287
+ update(table) {
288
+ return this.drizzle.updateWithCacheContext(table);
289
+ }
290
+ /**
291
+ * Creates a delete query builder.
292
+ *
293
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
294
+ * For versioned deletes, use `modifyWithVersioning().deleteById()` or `modifyWithVersioningAndEvictCache().deleteById()` instead.
295
+ *
296
+ * @param table - The table to delete from
297
+ * @returns Delete query builder (no versioning, no cache management)
298
+ */
299
+ delete(table) {
300
+ return this.drizzle.deleteWithCacheContext(table);
301
+ }
302
+ /**
303
+ * Creates a delete query builder that automatically evicts cache after execution.
304
+ *
305
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
306
+ * For versioned deletes, use `modifyWithVersioning().deleteById()` or `modifyWithVersioningAndEvictCache().deleteById()` instead.
307
+ *
308
+ * @param table - The table to delete from
309
+ * @returns Delete query builder with automatic cache eviction (no versioning)
310
+ */
311
+ deleteAndEvictCache(table) {
312
+ return this.drizzle.deleteAndEvictCache(table);
313
+ }
314
+ /**
315
+ * Create the modify operations instance.
316
+ * @returns modify operations.
317
+ */
318
+ modifyWithVersioning() {
319
+ return this.crudOperations;
320
+ }
321
+ /**
322
+ * Returns the singleton instance of ForgeSQLORMImpl.
323
+ * @param options - Options for configuring ForgeSQL ORM behavior.
324
+ * @returns The singleton instance of ForgeSQLORMImpl.
325
+ */
326
+ static getInstance(options) {
327
+ ForgeSQLORMImpl.instance ??= new ForgeSQLORMImpl(options);
328
+ return ForgeSQLORMImpl.instance;
329
+ }
330
+ /**
331
+ * Retrieves the fetch operations instance.
332
+ * @returns Fetch operations.
333
+ */
334
+ fetch() {
335
+ return this.fetchOperations;
336
+ }
337
+ /**
338
+ * Provides query analysis capabilities including EXPLAIN ANALYZE and slow query analysis.
339
+ * @returns {SchemaAnalyzeForgeSql} Interface for analyzing query performance
340
+ */
341
+ analyze() {
342
+ return this.analyzeOperations;
343
+ }
344
+ /**
345
+ * Provides schema-level SQL operations with optimistic locking/versioning and automatic cache eviction.
346
+ *
347
+ * This method returns operations that use `modifyWithVersioning()` internally, providing:
348
+ * - Optimistic locking support
349
+ * - Automatic version field management
350
+ * - Cache eviction after successful operations
351
+ *
352
+ * @returns {ForgeSQLCacheOperations} Interface for executing versioned SQL operations with cache management
353
+ */
354
+ modifyWithVersioningAndEvictCache() {
355
+ return this.cacheOperations;
356
+ }
357
+ /**
358
+ * Returns a Drizzle query builder instance.
359
+ *
360
+ * ⚠️ IMPORTANT: This method should be used ONLY for query building purposes.
361
+ * The returned instance should NOT be used for direct database connections or query execution.
362
+ * All database operations should be performed through Forge SQL's executeRawSQL or executeRawUpdateSQL methods.
363
+ *
364
+ * @returns A Drizzle query builder instance for query construction only.
365
+ */
366
+ getDrizzleQueryBuilder() {
367
+ return this.drizzle;
368
+ }
369
+ /**
370
+ * Creates a select query with unique field aliases to prevent field name collisions in joins.
371
+ * This is particularly useful when working with Atlassian Forge SQL, which collapses fields with the same name in joined tables.
372
+ *
373
+ * @template TSelection - The type of the selected fields
374
+ * @param {TSelection} fields - Object containing the fields to select, with table schemas as values
375
+ * @returns {MySqlSelectBuilder<TSelection, MySql2PreparedQueryHKT>} A select query builder with unique field aliases
376
+ * @throws {Error} If fields parameter is empty
377
+ * @example
378
+ * ```typescript
379
+ * await forgeSQL
380
+ * .select({user: users, order: orders})
381
+ * .from(orders)
382
+ * .innerJoin(users, eq(orders.userId, users.id));
383
+ * ```
384
+ */
385
+ select(fields) {
386
+ if (!fields) {
387
+ throw new Error("fields is empty");
388
+ }
389
+ return this.drizzle.selectAliased(fields);
390
+ }
391
+ /**
392
+ * Creates a distinct select query with unique field aliases to prevent field name collisions in joins.
393
+ * This is particularly useful when working with Atlassian Forge SQL, which collapses fields with the same name in joined tables.
394
+ *
395
+ * @template TSelection - The type of the selected fields
396
+ * @param {TSelection} fields - Object containing the fields to select, with table schemas as values
397
+ * @returns {MySqlSelectBuilder<TSelection, MySql2PreparedQueryHKT>} A distinct select query builder with unique field aliases
398
+ * @throws {Error} If fields parameter is empty
399
+ * @example
400
+ * ```typescript
401
+ * await forgeSQL
402
+ * .selectDistinct({user: users, order: orders})
403
+ * .from(orders)
404
+ * .innerJoin(users, eq(orders.userId, users.id));
405
+ * ```
406
+ */
407
+ selectDistinct(fields) {
408
+ if (!fields) {
409
+ throw new Error("fields is empty");
410
+ }
411
+ return this.drizzle.selectAliasedDistinct(fields);
412
+ }
413
+ /**
414
+ * Creates a cacheable select query with unique field aliases to prevent field name collisions in joins.
415
+ * This is particularly useful when working with Atlassian Forge SQL, which collapses fields with the same name in joined tables.
416
+ *
417
+ * @template TSelection - The type of the selected fields
418
+ * @param {TSelection} fields - Object containing the fields to select, with table schemas as values
419
+ * @param {number} cacheTTL - cache ttl optional default is 60 sec.
420
+ * @returns {MySqlSelectBuilder<TSelection, MySql2PreparedQueryHKT>} A select query builder with unique field aliases
421
+ * @throws {Error} If fields parameter is empty
422
+ * @example
423
+ * ```typescript
424
+ * await forgeSQL
425
+ * .selectCacheable({user: users, order: orders},60)
426
+ * .from(orders)
427
+ * .innerJoin(users, eq(orders.userId, users.id));
428
+ * ```
429
+ */
430
+ selectCacheable(fields, cacheTTL) {
431
+ if (!fields) {
432
+ throw new Error("fields is empty");
433
+ }
434
+ return this.drizzle.selectAliasedCacheable(fields, cacheTTL);
435
+ }
436
+ /**
437
+ * Creates a cacheable distinct select query with unique field aliases to prevent field name collisions in joins.
438
+ * This is particularly useful when working with Atlassian Forge SQL, which collapses fields with the same name in joined tables.
439
+ *
440
+ * @template TSelection - The type of the selected fields
441
+ * @param {TSelection} fields - Object containing the fields to select, with table schemas as values
442
+ * @param {number} cacheTTL - cache ttl optional default is 60 sec.
443
+ * @returns {MySqlSelectBuilder<TSelection, MySql2PreparedQueryHKT>} A distinct select query builder with unique field aliases
444
+ * @throws {Error} If fields parameter is empty
445
+ * @example
446
+ * ```typescript
447
+ * await forgeSQL
448
+ * .selectDistinctCacheable({user: users, order: orders}, 60)
449
+ * .from(orders)
450
+ * .innerJoin(users, eq(orders.userId, users.id));
451
+ * ```
452
+ */
453
+ selectDistinctCacheable(fields, cacheTTL) {
454
+ if (!fields) {
455
+ throw new Error("fields is empty");
456
+ }
457
+ return this.drizzle.selectAliasedDistinctCacheable(fields, cacheTTL);
458
+ }
459
+ /**
460
+ * Creates a select query builder for all columns from a table with field aliasing support.
461
+ * This is a convenience method that automatically selects all columns from the specified table.
462
+ *
463
+ * @template T - The type of the table
464
+ * @param table - The table to select from
465
+ * @returns Select query builder with all table columns and field aliasing support
466
+ * @example
467
+ * ```typescript
468
+ * const users = await forgeSQL.selectFrom(userTable).where(eq(userTable.id, 1));
469
+ * ```
470
+ */
471
+ selectFrom(table) {
472
+ return this.drizzle.selectFrom(table);
473
+ }
474
+ /**
475
+ * Creates a select distinct query builder for all columns from a table with field aliasing support.
476
+ * This is a convenience method that automatically selects all distinct columns from the specified table.
477
+ *
478
+ * @template T - The type of the table
479
+ * @param table - The table to select from
480
+ * @returns Select distinct query builder with all table columns and field aliasing support
481
+ * @example
482
+ * ```typescript
483
+ * const uniqueUsers = await forgeSQL.selectDistinctFrom(userTable).where(eq(userTable.status, 'active'));
484
+ * ```
485
+ */
486
+ selectDistinctFrom(table) {
487
+ return this.drizzle.selectDistinctFrom(table);
488
+ }
489
+ /**
490
+ * Creates a cacheable select query builder for all columns from a table with field aliasing and caching support.
491
+ * This is a convenience method that automatically selects all columns from the specified table with caching enabled.
492
+ *
493
+ * @template T - The type of the table
494
+ * @param table - The table to select from
495
+ * @param cacheTTL - Optional cache TTL override (defaults to global cache TTL)
496
+ * @returns Select query builder with all table columns, field aliasing, and caching support
497
+ * @example
498
+ * ```typescript
499
+ * const users = await forgeSQL.selectCacheableFrom(userTable, 300).where(eq(userTable.id, 1));
500
+ * ```
501
+ */
502
+ selectCacheableFrom(table, cacheTTL) {
503
+ return this.drizzle.selectFromCacheable(table, cacheTTL);
504
+ }
505
+ /**
506
+ * Creates a cacheable select distinct query builder for all columns from a table with field aliasing and caching support.
507
+ * This is a convenience method that automatically selects all distinct columns from the specified table with caching enabled.
508
+ *
509
+ * @template T - The type of the table
510
+ * @param table - The table to select from
511
+ * @param cacheTTL - Optional cache TTL override (defaults to global cache TTL)
512
+ * @returns Select distinct query builder with all table columns, field aliasing, and caching support
513
+ * @example
514
+ * ```typescript
515
+ * const uniqueUsers = await forgeSQL.selectDistinctCacheableFrom(userTable, 300).where(eq(userTable.status, 'active'));
516
+ * ```
517
+ */
518
+ selectDistinctCacheableFrom(table, cacheTTL) {
519
+ return this.drizzle.selectDistinctFromCacheable(table, cacheTTL);
520
+ }
521
+ /**
522
+ * Executes a raw SQL query with local cache support.
523
+ * This method provides local caching for raw SQL queries within the current invocation context.
524
+ * Results are cached locally and will be returned from cache on subsequent identical queries.
525
+ *
526
+ * @param query - The SQL query to execute (SQLWrapper or string)
527
+ * @returns Promise with query results
528
+ * @example
529
+ * ```typescript
530
+ * // Using SQLWrapper
531
+ * const result = await forgeSQL.execute(sql`SELECT * FROM users WHERE id = ${userId}`);
532
+ *
533
+ * // Using string
534
+ * const result = await forgeSQL.execute("SELECT * FROM users WHERE status = 'active'");
535
+ * ```
536
+ */
537
+ execute(query) {
538
+ return this.drizzle.executeQuery(query);
539
+ }
540
+ /**
541
+ * Executes a Data Definition Language (DDL) SQL query.
542
+ * DDL operations include CREATE, ALTER, DROP, TRUNCATE, and other schema modification statements.
543
+ *
544
+ * This method is specifically designed for DDL operations and provides:
545
+ * - Proper operation type context for DDL queries
546
+ * - No caching (DDL operations should not be cached)
547
+ * - Direct execution without query optimization
548
+ *
549
+ * @template T - The expected return type of the query result
550
+ * @param query - The DDL SQL query to execute (SQLWrapper or string)
551
+ * @returns Promise with query results
552
+ * @throws {Error} If the DDL operation fails
553
+ *
554
+ * @example
555
+ * ```typescript
556
+ * // Create a new table
557
+ * await forgeSQL.executeDDL(`
558
+ * CREATE TABLE users (
559
+ * id INT PRIMARY KEY AUTO_INCREMENT,
560
+ * name VARCHAR(255) NOT NULL,
561
+ * email VARCHAR(255) UNIQUE
562
+ * )
563
+ * `);
564
+ *
565
+ * // Alter table structure
566
+ * await forgeSQL.executeDDL(sql`
567
+ * ALTER TABLE users
568
+ * ADD COLUMN created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
569
+ * `);
570
+ *
571
+ * // Drop a table
572
+ * await forgeSQL.executeDDL("DROP TABLE IF EXISTS old_users");
573
+ * ```
574
+ */
575
+ async executeDDL(query) {
576
+ return this.executeDDLActions(async () => this.drizzle.executeQuery(query));
577
+ }
578
+ /**
579
+ * Executes a series of actions within a DDL operation context.
580
+ * This method provides a way to execute regular SQL queries that should be treated
581
+ * as DDL operations, ensuring proper operation type context for performance monitoring.
582
+ *
583
+ * This method is useful for:
584
+ * - Executing regular SQL queries in DDL context for monitoring purposes
585
+ * - Wrapping non-DDL operations that should be treated as DDL for analysis
586
+ * - Ensuring proper operation type context for complex workflows
587
+ * - Maintaining DDL operation context across multiple function calls
588
+ *
589
+ * @template T - The return type of the actions function
590
+ * @param actions - Function containing SQL operations to execute in DDL context
591
+ * @returns Promise that resolves to the return value of the actions function
592
+ *
593
+ * @example
594
+ * ```typescript
595
+ * // Execute regular SQL queries in DDL context for monitoring
596
+ * await forgeSQL.executeDDLActions(async () => {
597
+ * const slowQueries = await forgeSQL.execute(`
598
+ * SELECT * FROM INFORMATION_SCHEMA.STATEMENTS_SUMMARY
599
+ * WHERE AVG_LATENCY > 1000000
600
+ * `);
601
+ * return slowQueries;
602
+ * });
603
+ *
604
+ * // Execute complex analysis queries in DDL context
605
+ * const result = await forgeSQL.executeDDLActions(async () => {
606
+ * const tableInfo = await forgeSQL.execute("SHOW TABLES");
607
+ * const performanceData = await forgeSQL.execute(`
608
+ * SELECT * FROM INFORMATION_SCHEMA.CLUSTER_STATEMENTS_SUMMARY_HISTORY
609
+ * WHERE SUMMARY_END_TIME > DATE_SUB(NOW(), INTERVAL 1 HOUR)
610
+ * `);
611
+ * return { tableInfo, performanceData };
612
+ * });
613
+ *
614
+ * // Execute monitoring queries with error handling
615
+ * try {
616
+ * await forgeSQL.executeDDLActions(async () => {
617
+ * const metrics = await forgeSQL.execute(`
618
+ * SELECT COUNT(*) as query_count
619
+ * FROM INFORMATION_SCHEMA.STATEMENTS_SUMMARY
620
+ * `);
621
+ * console.log(`Total queries: ${metrics[0].query_count}`);
622
+ * });
623
+ * } catch (error) {
624
+ * console.error("Monitoring query failed:", error);
625
+ * }
626
+ * ```
627
+ */
628
+ async executeDDLActions(actions) {
629
+ return requestTypeContextUtils_1.operationTypeQueryContext.run({ operationType: "DDL" }, async () => actions());
630
+ }
631
+ /**
632
+ * Executes a raw SQL query with both local and global cache support.
633
+ * This method provides comprehensive caching for raw SQL queries:
634
+ * - Local cache: Within the current invocation context
635
+ * - Global cache: Cross-invocation caching using @forge/kvs
636
+ *
637
+ * @param query - The SQL query to execute (SQLWrapper or string)
638
+ * @param cacheTtl - Optional cache TTL override (defaults to global cache TTL)
639
+ * @returns Promise with query results
640
+ * @example
641
+ * ```typescript
642
+ * // Using SQLWrapper with custom TTL
643
+ * const result = await forgeSQL.executeCacheable(sql`SELECT * FROM users WHERE id = ${userId}`, 300);
644
+ *
645
+ * // Using string with default TTL
646
+ * const result = await forgeSQL.executeCacheable("SELECT * FROM users WHERE status = 'active'");
647
+ * ```
648
+ */
649
+ executeCacheable(query, cacheTtl) {
650
+ return this.drizzle.executeQueryCacheable(query, cacheTtl);
651
+ }
652
+ /**
653
+ * Creates a Common Table Expression (CTE) builder for complex queries.
654
+ * CTEs allow you to define temporary named result sets that exist within the scope of a single query.
655
+ *
656
+ * @returns WithBuilder for creating CTEs
657
+ * @example
658
+ * ```typescript
659
+ * const withQuery = forgeSQL.$with('userStats').as(
660
+ * forgeSQL.select({ userId: users.id, count: sql<number>`count(*)` })
661
+ * .from(users)
662
+ * .groupBy(users.id)
663
+ * );
664
+ * ```
665
+ */
666
+ get $with() {
667
+ return this.drizzle.$with;
668
+ }
669
+ /**
670
+ * Creates a query builder that uses Common Table Expressions (CTEs).
671
+ * CTEs allow you to define temporary named result sets that exist within the scope of a single query.
672
+ *
673
+ * @param queries - Array of CTE queries created with $with()
674
+ * @returns Query builder with CTE support
675
+ * @example
676
+ * ```typescript
677
+ * const withQuery = forgeSQL.$with('userStats').as(
678
+ * forgeSQL.select({ userId: users.id, count: sql<number>`count(*)` })
679
+ * .from(users)
680
+ * .groupBy(users.id)
681
+ * );
682
+ *
683
+ * const result = await forgeSQL.with(withQuery)
684
+ * .select({ userId: withQuery.userId, count: withQuery.count })
685
+ * .from(withQuery);
686
+ * ```
687
+ */
688
+ with(...queries) {
689
+ return this.drizzle.with(...queries);
690
+ }
691
+ /**
692
+ * Provides access to Rovo integration - a secure pattern for natural-language analytics.
693
+ *
694
+ * Rovo enables secure execution of dynamic SQL queries with comprehensive security validations:
695
+ * - Only SELECT queries are allowed
696
+ * - Queries are restricted to a single table
697
+ * - JOINs, subqueries, and window functions are blocked
698
+ * - Row-Level Security (RLS) support for data isolation
699
+ *
700
+ * @returns {RovoIntegration} Rovo integration instance for secure dynamic queries
701
+ *
702
+ * @example
703
+ * ```typescript
704
+ * const rovo = forgeSQL.rovo();
705
+ * const settings = await rovo.rovoSettingBuilder(usersTable, accountId)
706
+ * .useRLS()
707
+ * .addRlsColumn(usersTable.id)
708
+ * .addRlsWherePart((alias) => `${alias}.id = '${accountId}'`)
709
+ * .finish()
710
+ * .build();
711
+ *
712
+ * const result = await rovo.dynamicIsolatedQuery(
713
+ * "SELECT id, name FROM users WHERE status = 'active'",
714
+ * settings
715
+ * );
716
+ * ```
717
+ */
718
+ rovo() {
719
+ return new Rovo_1.Rovo(this, this.options);
720
+ }
721
+ }
722
+ /**
723
+ * Public class that acts as a wrapper around the private ForgeSQLORMImpl.
724
+ * Provides a clean interface for working with Forge SQL and Drizzle ORM.
725
+ */
726
+ class ForgeSQLORM {
727
+ ormInstance;
728
+ constructor(options) {
729
+ this.ormInstance = ForgeSQLORMImpl.getInstance(options);
730
+ }
731
+ /**
732
+ * Executes a query and provides access to execution metadata with performance monitoring.
733
+ * This method allows you to capture detailed information about query execution
734
+ * including database execution time, response size, and query analysis capabilities.
735
+ *
736
+ * The method aggregates metrics across all database operations within the query function,
737
+ * making it ideal for monitoring resolver performance and detecting performance issues.
738
+ *
739
+ * @template T - The return type of the query
740
+ * @param query - A function that returns a Promise with the query result. Can contain multiple database operations.
741
+ * @param onMetadata - Callback function that receives aggregated execution metadata
742
+ * @param onMetadata.totalDbExecutionTime - Total database execution time across all operations in the query function (in milliseconds)
743
+ * @param onMetadata.totalResponseSize - Total response size across all operations (in bytes)
744
+ * @param onMetadata.printQueries - Function to analyze and print query execution plans from CLUSTER_STATEMENTS_SUMMARY
745
+ * @returns Promise with the query result
746
+ *
747
+ * @example
748
+ * ```typescript
749
+ * // Basic usage with performance monitoring
750
+ * const result = await forgeSQL.executeWithMetadata(
751
+ * async () => {
752
+ * const users = await forgeSQL.selectFrom(usersTable);
753
+ * const orders = await forgeSQL.selectFrom(ordersTable).where(eq(ordersTable.userId, usersTable.id));
754
+ * return { users, orders };
755
+ * },
756
+ * (totalDbExecutionTime, totalResponseSize, printQueries) => {
757
+ * const threshold = 500; // ms baseline for this resolver
758
+ *
759
+ * if (totalDbExecutionTime > threshold * 1.5) {
760
+ * console.warn(`[Performance Warning] Resolver exceeded DB time: ${totalDbExecutionTime} ms`);
761
+ * await printQueries(); // Analyze and print query execution plans
762
+ * } else if (totalDbExecutionTime > threshold) {
763
+ * console.debug(`[Performance Debug] High DB time: ${totalDbExecutionTime} ms`);
764
+ * }
765
+ *
766
+ * console.log(`DB response size: ${totalResponseSize} bytes`);
767
+ * }
768
+ * );
769
+ * ```
770
+ *
771
+ * @example
772
+ * ```typescript
773
+ * // Resolver with performance monitoring
774
+ * resolver.define("fetch", async (req: Request) => {
775
+ * try {
776
+ * return await forgeSQL.executeWithMetadata(
777
+ * async () => {
778
+ * // Resolver logic with multiple queries
779
+ * const users = await forgeSQL.selectFrom(demoUsers);
780
+ * const orders = await forgeSQL.selectFrom(demoOrders)
781
+ * .where(eq(demoOrders.userId, demoUsers.id));
782
+ * return { users, orders };
783
+ * },
784
+ * async (totalDbExecutionTime, totalResponseSize, printQueries) => {
785
+ * const threshold = 500; // ms baseline for this resolver
786
+ *
787
+ * if (totalDbExecutionTime > threshold * 1.5) {
788
+ * console.warn(`[Performance Warning fetch] Resolver exceeded DB time: ${totalDbExecutionTime} ms`);
789
+ * await printQueries(); // Optionally log or capture diagnostics for further analysis
790
+ * } else if (totalDbExecutionTime > threshold) {
791
+ * console.debug(`[Performance Debug] High DB time: ${totalDbExecutionTime} ms`);
792
+ * }
793
+ *
794
+ * console.log(`DB response size: ${totalResponseSize} bytes`);
795
+ * }
796
+ * );
797
+ * } catch (e) {
798
+ * const error = e?.cause?.debug?.sqlMessage ?? e?.cause;
799
+ * console.error(error, e);
800
+ * throw error;
801
+ * }
802
+ * });
803
+ * ```
804
+ *
805
+ * @note **Important**: When multiple resolvers are running concurrently, their query data may also appear in `printQueries()` analysis, as it queries the global `CLUSTER_STATEMENTS_SUMMARY` table.
806
+ */
807
+ async executeWithMetadata(query, onMetadata) {
808
+ return this.ormInstance.executeWithMetadata(query, onMetadata);
809
+ }
810
+ selectCacheable(fields, cacheTTL) {
811
+ return this.ormInstance.selectCacheable(fields, cacheTTL);
812
+ }
813
+ selectDistinctCacheable(fields, cacheTTL) {
814
+ return this.ormInstance.selectDistinctCacheable(fields, cacheTTL);
815
+ }
816
+ /**
817
+ * Creates a select query builder for all columns from a table with field aliasing support.
818
+ * This is a convenience method that automatically selects all columns from the specified table.
819
+ *
820
+ * @template T - The type of the table
821
+ * @param table - The table to select from
822
+ * @returns Select query builder with all table columns and field aliasing support
823
+ * @example
824
+ * ```typescript
825
+ * const users = await forgeSQL.selectFrom(userTable).where(eq(userTable.id, 1));
826
+ * ```
827
+ */
828
+ selectFrom(table) {
829
+ return this.ormInstance.getDrizzleQueryBuilder().selectFrom(table);
830
+ }
831
+ /**
832
+ * Creates a select distinct query builder for all columns from a table with field aliasing support.
833
+ * This is a convenience method that automatically selects all distinct columns from the specified table.
834
+ *
835
+ * @template T - The type of the table
836
+ * @param table - The table to select from
837
+ * @returns Select distinct query builder with all table columns and field aliasing support
838
+ * @example
839
+ * ```typescript
840
+ * const uniqueUsers = await forgeSQL.selectDistinctFrom(userTable).where(eq(userTable.status, 'active'));
841
+ * ```
842
+ */
843
+ selectDistinctFrom(table) {
844
+ return this.ormInstance.getDrizzleQueryBuilder().selectDistinctFrom(table);
845
+ }
846
+ /**
847
+ * Creates a cacheable select query builder for all columns from a table with field aliasing and caching support.
848
+ * This is a convenience method that automatically selects all columns from the specified table with caching enabled.
849
+ *
850
+ * @template T - The type of the table
851
+ * @param table - The table to select from
852
+ * @param cacheTTL - Optional cache TTL override (defaults to global cache TTL)
853
+ * @returns Select query builder with all table columns, field aliasing, and caching support
854
+ * @example
855
+ * ```typescript
856
+ * const users = await forgeSQL.selectCacheableFrom(userTable, 300).where(eq(userTable.id, 1));
857
+ * ```
858
+ */
859
+ selectCacheableFrom(table, cacheTTL) {
860
+ return this.ormInstance.getDrizzleQueryBuilder().selectFromCacheable(table, cacheTTL);
861
+ }
862
+ /**
863
+ * Creates a cacheable select distinct query builder for all columns from a table with field aliasing and caching support.
864
+ * This is a convenience method that automatically selects all distinct columns from the specified table with caching enabled.
865
+ *
866
+ * @template T - The type of the table
867
+ * @param table - The table to select from
868
+ * @param cacheTTL - Optional cache TTL override (defaults to global cache TTL)
869
+ * @returns Select distinct query builder with all table columns, field aliasing, and caching support
870
+ * @example
871
+ * ```typescript
872
+ * const uniqueUsers = await forgeSQL.selectDistinctCacheableFrom(userTable, 300).where(eq(userTable.status, 'active'));
873
+ * ```
874
+ */
875
+ selectDistinctCacheableFrom(table, cacheTTL) {
876
+ return this.ormInstance.getDrizzleQueryBuilder().selectDistinctFromCacheable(table, cacheTTL);
877
+ }
878
+ executeWithCacheContext(cacheContext) {
879
+ return this.ormInstance.executeWithCacheContext(cacheContext);
880
+ }
881
+ executeWithCacheContextAndReturnValue(cacheContext) {
882
+ return this.ormInstance.executeWithCacheContextAndReturnValue(cacheContext);
883
+ }
884
+ /**
885
+ * Executes operations within a local cache context.
886
+ * This provides in-memory caching for select queries within a single request scope.
887
+ *
888
+ * @param cacheContext - Function containing operations that will benefit from local caching
889
+ * @returns Promise that resolves when all operations are complete
890
+ */
891
+ executeWithLocalContext(cacheContext) {
892
+ return this.ormInstance.executeWithLocalContext(cacheContext);
893
+ }
894
+ /**
895
+ * Executes operations within a local cache context and returns a value.
896
+ * This provides in-memory caching for select queries within a single request scope.
897
+ *
898
+ * @param cacheContext - Function containing operations that will benefit from local caching
899
+ * @returns Promise that resolves to the return value of the cacheContext function
900
+ */
901
+ executeWithLocalCacheContextAndReturnValue(cacheContext) {
902
+ return this.ormInstance.executeWithLocalCacheContextAndReturnValue(cacheContext);
903
+ }
904
+ /**
905
+ * Creates an insert query builder.
906
+ *
907
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
908
+ * For versioned inserts, use `modifyWithVersioning().insert()` or `modifyWithVersioningAndEvictCache().insert()` instead.
909
+ *
910
+ * @param table - The table to insert into
911
+ * @returns Insert query builder (no versioning, no cache management)
912
+ */
913
+ insert(table) {
914
+ return this.ormInstance.insert(table);
915
+ }
916
+ /**
917
+ * Creates an insert query builder that automatically evicts cache after execution.
918
+ *
919
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
920
+ * For versioned inserts, use `modifyWithVersioning().insert()` or `modifyWithVersioningAndEvictCache().insert()` instead.
921
+ *
922
+ * @param table - The table to insert into
923
+ * @returns Insert query builder with automatic cache eviction (no versioning)
924
+ */
925
+ insertAndEvictCache(table) {
926
+ return this.ormInstance.insertAndEvictCache(table);
927
+ }
928
+ /**
929
+ * Creates an update query builder.
930
+ *
931
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
932
+ * For versioned updates, use `modifyWithVersioning().updateById()` or `modifyWithVersioningAndEvictCache().updateById()` instead.
933
+ *
934
+ * @param table - The table to update
935
+ * @returns Update query builder (no versioning, no cache management)
936
+ */
937
+ update(table) {
938
+ return this.ormInstance.update(table);
939
+ }
940
+ /**
941
+ * Creates an update query builder that automatically evicts cache after execution.
942
+ *
943
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
944
+ * For versioned updates, use `modifyWithVersioning().updateById()` or `modifyWithVersioningAndEvictCache().updateById()` instead.
945
+ *
946
+ * @param table - The table to update
947
+ * @returns Update query builder with automatic cache eviction (no versioning)
948
+ */
949
+ updateAndEvictCache(table) {
950
+ return this.ormInstance.updateAndEvictCache(table);
951
+ }
952
+ /**
953
+ * Creates a delete query builder.
954
+ *
955
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
956
+ * For versioned deletes, use `modifyWithVersioning().deleteById()` or `modifyWithVersioningAndEvictCache().deleteById()` instead.
957
+ *
958
+ * @param table - The table to delete from
959
+ * @returns Delete query builder (no versioning, no cache management)
960
+ */
961
+ delete(table) {
962
+ return this.ormInstance.delete(table);
963
+ }
964
+ /**
965
+ * Creates a delete query builder that automatically evicts cache after execution.
966
+ *
967
+ * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
968
+ * For versioned deletes, use `modifyWithVersioning().deleteById()` or `modifyWithVersioningAndEvictCache().deleteById()` instead.
969
+ *
970
+ * @param table - The table to delete from
971
+ * @returns Delete query builder with automatic cache eviction (no versioning)
972
+ */
973
+ deleteAndEvictCache(table) {
974
+ return this.ormInstance.deleteAndEvictCache(table);
975
+ }
976
+ /**
977
+ * Creates a select query with unique field aliases to prevent field name collisions in joins.
978
+ * This is particularly useful when working with Atlassian Forge SQL, which collapses fields with the same name in joined tables.
979
+ *
980
+ * @template TSelection - The type of the selected fields
981
+ * @param {TSelection} fields - Object containing the fields to select, with table schemas as values
982
+ * @returns {MySqlSelectBuilder<TSelection, MySql2PreparedQueryHKT>} A select query builder with unique field aliases
983
+ * @throws {Error} If fields parameter is empty
984
+ * @example
985
+ * ```typescript
986
+ * await forgeSQL
987
+ * .select({user: users, order: orders})
988
+ * .from(orders)
989
+ * .innerJoin(users, eq(orders.userId, users.id));
990
+ * ```
991
+ */
992
+ select(fields) {
993
+ return this.ormInstance.select(fields);
994
+ }
995
+ /**
996
+ * Creates a distinct select query with unique field aliases to prevent field name collisions in joins.
997
+ * This is particularly useful when working with Atlassian Forge SQL, which collapses fields with the same name in joined tables.
998
+ *
999
+ * @template TSelection - The type of the selected fields
1000
+ * @param {TSelection} fields - Object containing the fields to select, with table schemas as values
1001
+ * @returns {MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>} A distinct select query builder with unique field aliases
1002
+ * @throws {Error} If fields parameter is empty
1003
+ * @example
1004
+ * ```typescript
1005
+ * await forgeSQL
1006
+ * .selectDistinct({user: users, order: orders})
1007
+ * .from(orders)
1008
+ * .innerJoin(users, eq(orders.userId, users.id));
1009
+ * ```
1010
+ */
1011
+ selectDistinct(fields) {
1012
+ return this.ormInstance.selectDistinct(fields);
1013
+ }
1014
+ /**
1015
+ * Proxies the `modify` method from `ForgeSQLORMImpl`.
1016
+ * @returns Modify operations.
1017
+ */
1018
+ modifyWithVersioning() {
1019
+ return this.ormInstance.modifyWithVersioning();
1020
+ }
1021
+ /**
1022
+ * Proxies the `fetch` method from `ForgeSQLORMImpl`.
1023
+ * @returns Fetch operations.
1024
+ */
1025
+ fetch() {
1026
+ return this.ormInstance.fetch();
1027
+ }
1028
+ /**
1029
+ * Provides query analysis capabilities including EXPLAIN ANALYZE and slow query analysis.
1030
+ * @returns {SchemaAnalyzeForgeSql} Interface for analyzing query performance
1031
+ */
1032
+ analyze() {
1033
+ return this.ormInstance.analyze();
1034
+ }
1035
+ /**
1036
+ * Provides schema-level SQL cacheable operations with type safety.
1037
+ * @returns {ForgeSQLCacheOperations} Interface for executing schema-bound SQL queries
1038
+ */
1039
+ modifyWithVersioningAndEvictCache() {
1040
+ return this.ormInstance.modifyWithVersioningAndEvictCache();
1041
+ }
1042
+ /**
1043
+ * Returns a Drizzle query builder instance.
1044
+ *
1045
+ * @returns A Drizzle query builder instance for query construction only.
1046
+ */
1047
+ getDrizzleQueryBuilder() {
1048
+ return this.ormInstance.getDrizzleQueryBuilder();
1049
+ }
1050
+ /**
1051
+ * Executes a raw SQL query with local cache support.
1052
+ * This method provides local caching for raw SQL queries within the current invocation context.
1053
+ * Results are cached locally and will be returned from cache on subsequent identical queries.
1054
+ *
1055
+ * @param query - The SQL query to execute (SQLWrapper or string)
1056
+ * @returns Promise with query results
1057
+ * @example
1058
+ * ```typescript
1059
+ * // Using SQLWrapper
1060
+ * const result = await forgeSQL.execute(sql`SELECT * FROM users WHERE id = ${userId}`);
1061
+ *
1062
+ * // Using string
1063
+ * const result = await forgeSQL.execute("SELECT * FROM users WHERE status = 'active'");
1064
+ * ```
1065
+ */
1066
+ execute(query) {
1067
+ return this.ormInstance.execute(query);
1068
+ }
1069
+ /**
1070
+ * Executes a Data Definition Language (DDL) SQL query.
1071
+ * DDL operations include CREATE, ALTER, DROP, TRUNCATE, and other schema modification statements.
1072
+ *
1073
+ * This method is specifically designed for DDL operations and provides:
1074
+ * - Proper operation type context for DDL queries
1075
+ * - No caching (DDL operations should not be cached)
1076
+ * - Direct execution without query optimization
1077
+ *
1078
+ * @template T - The expected return type of the query result
1079
+ * @param query - The DDL SQL query to execute (SQLWrapper or string)
1080
+ * @returns Promise with query results
1081
+ * @throws {Error} If the DDL operation fails
1082
+ *
1083
+ * @example
1084
+ * ```typescript
1085
+ * // Create a new table
1086
+ * await forgeSQL.executeDDL(`
1087
+ * CREATE TABLE users (
1088
+ * id INT PRIMARY KEY AUTO_INCREMENT,
1089
+ * name VARCHAR(255) NOT NULL,
1090
+ * email VARCHAR(255) UNIQUE
1091
+ * )
1092
+ * `);
1093
+ *
1094
+ * // Alter table structure
1095
+ * await forgeSQL.executeDDL(sql`
1096
+ * ALTER TABLE users
1097
+ * ADD COLUMN created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
1098
+ * `);
1099
+ *
1100
+ * // Drop a table
1101
+ * await forgeSQL.executeDDL("DROP TABLE IF EXISTS old_users");
1102
+ * ```
1103
+ */
1104
+ executeDDL(query) {
1105
+ return this.ormInstance.executeDDL(query);
1106
+ }
1107
+ /**
1108
+ * Executes a series of actions within a DDL operation context.
1109
+ * This method provides a way to execute regular SQL queries that should be treated
1110
+ * as DDL operations, ensuring proper operation type context for performance monitoring.
1111
+ *
1112
+ * This method is useful for:
1113
+ * - Executing regular SQL queries in DDL context for monitoring purposes
1114
+ * - Wrapping non-DDL operations that should be treated as DDL for analysis
1115
+ * - Ensuring proper operation type context for complex workflows
1116
+ * - Maintaining DDL operation context across multiple function calls
1117
+ *
1118
+ * @template T - The return type of the actions function
1119
+ * @param actions - Function containing SQL operations to execute in DDL context
1120
+ * @returns Promise that resolves to the return value of the actions function
1121
+ *
1122
+ * @example
1123
+ * ```typescript
1124
+ * // Execute regular SQL queries in DDL context for monitoring
1125
+ * await forgeSQL.executeDDLActions(async () => {
1126
+ * const slowQueries = await forgeSQL.execute(`
1127
+ * SELECT * FROM INFORMATION_SCHEMA.STATEMENTS_SUMMARY
1128
+ * WHERE AVG_LATENCY > 1000000
1129
+ * `);
1130
+ * return slowQueries;
1131
+ * });
1132
+ *
1133
+ * // Execute complex analysis queries in DDL context
1134
+ * const result = await forgeSQL.executeDDLActions(async () => {
1135
+ * const tableInfo = await forgeSQL.execute("SHOW TABLES");
1136
+ * const performanceData = await forgeSQL.execute(`
1137
+ * SELECT * FROM INFORMATION_SCHEMA.CLUSTER_STATEMENTS_SUMMARY_HISTORY
1138
+ * WHERE SUMMARY_END_TIME > DATE_SUB(NOW(), INTERVAL 1 HOUR)
1139
+ * `);
1140
+ * return { tableInfo, performanceData };
1141
+ * });
1142
+ *
1143
+ * // Execute monitoring queries with error handling
1144
+ * try {
1145
+ * await forgeSQL.executeDDLActions(async () => {
1146
+ * const metrics = await forgeSQL.execute(`
1147
+ * SELECT COUNT(*) as query_count
1148
+ * FROM INFORMATION_SCHEMA.STATEMENTS_SUMMARY
1149
+ * `);
1150
+ * console.log(`Total queries: ${metrics[0].query_count}`);
1151
+ * });
1152
+ * } catch (error) {
1153
+ * console.error("Monitoring query failed:", error);
1154
+ * }
1155
+ * ```
1156
+ */
1157
+ executeDDLActions(actions) {
1158
+ return this.ormInstance.executeDDLActions(actions);
1159
+ }
1160
+ /**
1161
+ * Executes a raw SQL query with both local and global cache support.
1162
+ * This method provides comprehensive caching for raw SQL queries:
1163
+ * - Local cache: Within the current invocation context
1164
+ * - Global cache: Cross-invocation caching using @forge/kvs
1165
+ *
1166
+ * @param query - The SQL query to execute (SQLWrapper or string)
1167
+ * @param cacheTtl - Optional cache TTL override (defaults to global cache TTL)
1168
+ * @returns Promise with query results
1169
+ * @example
1170
+ * ```typescript
1171
+ * // Using SQLWrapper with custom TTL
1172
+ * const result = await forgeSQL.executeCacheable(sql`SELECT * FROM users WHERE id = ${userId}`, 300);
1173
+ *
1174
+ * // Using string with default TTL
1175
+ * const result = await forgeSQL.executeCacheable("SELECT * FROM users WHERE status = 'active'");
1176
+ * ```
1177
+ */
1178
+ executeCacheable(query, cacheTtl) {
1179
+ return this.ormInstance.executeCacheable(query, cacheTtl);
1180
+ }
1181
+ /**
1182
+ * Creates a Common Table Expression (CTE) builder for complex queries.
1183
+ * CTEs allow you to define temporary named result sets that exist within the scope of a single query.
1184
+ *
1185
+ * @returns WithBuilder for creating CTEs
1186
+ * @example
1187
+ * ```typescript
1188
+ * const withQuery = forgeSQL.$with('userStats').as(
1189
+ * forgeSQL.getDrizzleQueryBuilder().select({ userId: users.id, count: sql<number>`count(*)` })
1190
+ * .from(users)
1191
+ * .groupBy(users.id)
1192
+ * );
1193
+ * ```
1194
+ */
1195
+ get $with() {
1196
+ return this.ormInstance.getDrizzleQueryBuilder().$with;
1197
+ }
1198
+ /**
1199
+ * Creates a query builder that uses Common Table Expressions (CTEs).
1200
+ * CTEs allow you to define temporary named result sets that exist within the scope of a single query.
1201
+ *
1202
+ * @param queries - Array of CTE queries created with $with()
1203
+ * @returns Query builder with CTE support
1204
+ * @example
1205
+ * ```typescript
1206
+ * const withQuery = forgeSQL.$with('userStats').as(
1207
+ * forgeSQL.getDrizzleQueryBuilder().select({ userId: users.id, count: sql<number>`count(*)` })
1208
+ * .from(users)
1209
+ * .groupBy(users.id)
1210
+ * );
1211
+ *
1212
+ * const result = await forgeSQL.with(withQuery)
1213
+ * .select({ userId: withQuery.userId, count: withQuery.count })
1214
+ * .from(withQuery);
1215
+ * ```
1216
+ */
1217
+ with(...queries) {
1218
+ return this.ormInstance.getDrizzleQueryBuilder().with(...queries);
1219
+ }
1220
+ /**
1221
+ * Provides access to Rovo integration - a secure pattern for natural-language analytics.
1222
+ *
1223
+ * Rovo enables secure execution of dynamic SQL queries with comprehensive security validations:
1224
+ * - Only SELECT queries are allowed
1225
+ * - Queries are restricted to a single table
1226
+ * - JOINs, subqueries, and window functions are blocked
1227
+ * - Row-Level Security (RLS) support for data isolation
1228
+ *
1229
+ * @returns {RovoIntegration} Rovo integration instance for secure dynamic queries
1230
+ *
1231
+ * @example
1232
+ * ```typescript
1233
+ * const rovo = forgeSQL.rovo();
1234
+ * const settings = await rovo.rovoSettingBuilder(usersTable, accountId)
1235
+ * .useRLS()
1236
+ * .addRlsColumn(usersTable.id)
1237
+ * .addRlsWherePart((alias) => `${alias}.id = '${accountId}'`)
1238
+ * .finish()
1239
+ * .build();
1240
+ *
1241
+ * const result = await rovo.dynamicIsolatedQuery(
1242
+ * "SELECT id, name FROM users WHERE status = 'active'",
1243
+ * settings
1244
+ * );
1245
+ * ```
1246
+ */
1247
+ rovo() {
1248
+ return this.ormInstance.rovo();
1249
+ }
1250
+ }
1251
+ exports.default = ForgeSQLORM;
1252
+ //# sourceMappingURL=ForgeSQLORM.js.map