forge-sql-orm 2.1.12 → 2.1.13

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