forge-sql-orm 2.1.15 → 2.1.17

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 (57) hide show
  1. package/README.md +194 -1
  2. package/dist/async/PrintQueryConsumer.d.ts +98 -0
  3. package/dist/async/PrintQueryConsumer.d.ts.map +1 -0
  4. package/dist/async/PrintQueryConsumer.js +89 -0
  5. package/dist/async/PrintQueryConsumer.js.map +1 -0
  6. package/dist/core/ForgeSQLQueryBuilder.d.ts +2 -3
  7. package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
  8. package/dist/core/ForgeSQLQueryBuilder.js.map +1 -1
  9. package/dist/core/ForgeSQLSelectOperations.d.ts +2 -1
  10. package/dist/core/ForgeSQLSelectOperations.d.ts.map +1 -1
  11. package/dist/core/ForgeSQLSelectOperations.js.map +1 -1
  12. package/dist/core/Rovo.d.ts +40 -0
  13. package/dist/core/Rovo.d.ts.map +1 -1
  14. package/dist/core/Rovo.js +164 -138
  15. package/dist/core/Rovo.js.map +1 -1
  16. package/dist/index.d.ts +2 -2
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +4 -2
  19. package/dist/index.js.map +1 -1
  20. package/dist/lib/drizzle/extensions/additionalActions.d.ts.map +1 -1
  21. package/dist/lib/drizzle/extensions/additionalActions.js +72 -22
  22. package/dist/lib/drizzle/extensions/additionalActions.js.map +1 -1
  23. package/dist/utils/cacheTableUtils.d.ts +11 -0
  24. package/dist/utils/cacheTableUtils.d.ts.map +1 -0
  25. package/dist/utils/cacheTableUtils.js +450 -0
  26. package/dist/utils/cacheTableUtils.js.map +1 -0
  27. package/dist/utils/cacheUtils.d.ts.map +1 -1
  28. package/dist/utils/cacheUtils.js +3 -22
  29. package/dist/utils/cacheUtils.js.map +1 -1
  30. package/dist/utils/forgeDriver.d.ts.map +1 -1
  31. package/dist/utils/forgeDriver.js +5 -12
  32. package/dist/utils/forgeDriver.js.map +1 -1
  33. package/dist/utils/forgeDriverProxy.js +7 -5
  34. package/dist/utils/forgeDriverProxy.js.map +1 -1
  35. package/dist/utils/metadataContextUtils.d.ts +44 -4
  36. package/dist/utils/metadataContextUtils.d.ts.map +1 -1
  37. package/dist/utils/metadataContextUtils.js +155 -50
  38. package/dist/utils/metadataContextUtils.js.map +1 -1
  39. package/dist/utils/sqlUtils.d.ts +3 -1
  40. package/dist/utils/sqlUtils.d.ts.map +1 -1
  41. package/dist/utils/sqlUtils.js +264 -144
  42. package/dist/utils/sqlUtils.js.map +1 -1
  43. package/dist/webtriggers/applyMigrationsWebTrigger.js +1 -1
  44. package/package.json +14 -13
  45. package/src/async/PrintQueryConsumer.ts +114 -0
  46. package/src/core/ForgeSQLQueryBuilder.ts +2 -2
  47. package/src/core/ForgeSQLSelectOperations.ts +2 -1
  48. package/src/core/Rovo.ts +209 -167
  49. package/src/index.ts +2 -3
  50. package/src/lib/drizzle/extensions/additionalActions.ts +98 -42
  51. package/src/utils/cacheTableUtils.ts +511 -0
  52. package/src/utils/cacheUtils.ts +3 -25
  53. package/src/utils/forgeDriver.ts +5 -11
  54. package/src/utils/forgeDriverProxy.ts +9 -9
  55. package/src/utils/metadataContextUtils.ts +169 -52
  56. package/src/utils/sqlUtils.ts +372 -177
  57. package/src/webtriggers/applyMigrationsWebTrigger.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forge-sql-orm",
3
- "version": "2.1.15",
3
+ "version": "2.1.17",
4
4
  "description": "Drizzle ORM integration for Atlassian @forge/sql. Provides a custom driver, schema migration, two levels of caching (local and global via @forge/kvs), optimistic locking, and query analysis.",
5
5
  "main": "dist/index.js",
6
6
  "homepage": "https://github.com/vzakharchenko/forge-sql-orm#readme",
@@ -25,25 +25,25 @@
25
25
  "database"
26
26
  ],
27
27
  "devDependencies": {
28
- "@eslint/js": "^9.39.1",
28
+ "@eslint/js": "^9.39.2",
29
29
  "@types/luxon": "^3.7.1",
30
- "@types/node": "^24.10.1",
31
- "@typescript-eslint/eslint-plugin": "^8.48.0",
32
- "@typescript-eslint/parser": "^8.48.0",
33
- "@vitest/coverage-v8": "^4.0.14",
34
- "@vitest/ui": "^4.0.14",
35
- "eslint": "^9.39.1",
30
+ "@types/node": "^25.0.3",
31
+ "@typescript-eslint/eslint-plugin": "^8.50.0",
32
+ "@typescript-eslint/parser": "^8.50.0",
33
+ "@vitest/coverage-v8": "^4.0.16",
34
+ "@vitest/ui": "^4.0.16",
35
+ "eslint": "^9.39.2",
36
36
  "eslint-config-prettier": "^10.1.8",
37
37
  "eslint-plugin-import": "^2.32.0",
38
38
  "eslint-plugin-vitest": "^0.5.4",
39
39
  "husky": "^9.1.7",
40
- "knip": "^5.70.2",
40
+ "knip": "^5.74.0",
41
41
  "patch-package": "^8.0.1",
42
- "prettier": "^3.7.2",
42
+ "prettier": "^3.7.4",
43
43
  "ts-node": "^10.9.2",
44
44
  "typescript": "^5.9.3",
45
45
  "uuid": "^13.0.0",
46
- "vitest": "^4.0.14"
46
+ "vitest": "^4.0.16"
47
47
  },
48
48
  "license": "MIT",
49
49
  "author": "Vasyl Zakharchenko",
@@ -71,13 +71,14 @@
71
71
  "README.md"
72
72
  ],
73
73
  "peerDependencies": {
74
- "@forge/sql": "^3.0.12",
75
- "drizzle-orm": "^0.44.7"
74
+ "@forge/sql": "^3.0.14",
75
+ "drizzle-orm": "^0.45.1"
76
76
  },
77
77
  "optionalDependencies": {
78
78
  "@forge/kvs": "^1.2.0"
79
79
  },
80
80
  "dependencies": {
81
+ "@forge/events": "^2.0.14",
81
82
  "luxon": "^3.7.2",
82
83
  "node-sql-parser": "^5.3.13"
83
84
  },
@@ -0,0 +1,114 @@
1
+ import { AsyncEvent } from "@forge/events";
2
+ import {
3
+ MetadataQueryOptions,
4
+ printDegradationQueries,
5
+ Statistic,
6
+ } from "../utils/metadataContextUtils";
7
+ import { ForgeSqlOperation } from "../core/ForgeSQLQueryBuilder";
8
+
9
+ /**
10
+ * Event payload for async query degradation analysis.
11
+ * Contains query performance statistics and metadata for analysis.
12
+ */
13
+ export type AsyncEventPrintQuery = {
14
+ /** Total database execution time across all queries in milliseconds */
15
+ totalDbExecutionTime: number;
16
+ /** Total response size across all queries in bytes */
17
+ totalResponseSize: number;
18
+ /** Timestamp when the query execution started */
19
+ beginTime: Date;
20
+ /** Query analysis options with all fields set to defaults if not provided */
21
+ options: Required<MetadataQueryOptions>;
22
+ /** Array of query statistics including SQL, parameters, and execution metadata */
23
+ statistics: Statistic[];
24
+ };
25
+
26
+ /**
27
+ * Consumer function for processing async query degradation events.
28
+ *
29
+ * This function is called by the Forge event system when a query degradation event
30
+ * is received from the queue. It processes the event and prints query performance
31
+ * analysis including execution plans for slow queries.
32
+ *
33
+ * The function logs a warning message with job ID, total DB time, query count, and
34
+ * start time to help correlate with the original resolver/service logs.
35
+ *
36
+ * @param forgeSQLORM - The ForgeSQL operation instance for database access
37
+ * @param event - The async event containing query degradation data
38
+ * @returns Promise that resolves when query analysis is complete
39
+ *
40
+ * @example
41
+ * ```yaml
42
+ * # manifest.yml - Configure consumer for async query degradation analysis
43
+ * modules:
44
+ * consumer:
45
+ * - key: print-degradation-queries
46
+ * queue: degradationQueue
47
+ * function: handlerAsyncDegradation
48
+ * function:
49
+ * - key: handlerAsyncDegradation
50
+ * handler: index.handlerAsyncDegradation
51
+ * ```
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * // index.ts - Handler function that processes async events
56
+ * import { AsyncEvent } from "@forge/events";
57
+ * import { printDegradationQueriesConsumer } from "forge-sql-orm";
58
+ * import { FORGE_SQL_ORM } from "./utils/forgeSqlOrmUtils";
59
+ *
60
+ * export const handlerAsyncDegradation = (event: AsyncEvent) => {
61
+ * return printDegradationQueriesConsumer(FORGE_SQL_ORM, event);
62
+ * };
63
+ * ```
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * // Using async queue in resolver - Enable async processing
68
+ * resolver.define("fetch", async (req: Request) => {
69
+ * return await FORGE_SQL_ORM.executeWithMetadata(
70
+ * async () => {
71
+ * // ... your queries ...
72
+ * return await SQL_QUERY;
73
+ * },
74
+ * async (totalDbExecutionTime, totalResponseSize, printQueries) => {
75
+ * if (totalDbExecutionTime > 800) {
76
+ * await printQueries(); // Will queue for async processing
77
+ * }
78
+ * },
79
+ * { asyncQueueName: "degradationQueue" } // Enable async processing
80
+ * );
81
+ * });
82
+ * ```
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * // Log correlation - How to find related logs
87
+ * //
88
+ * // 1. In resolver/service log, you'll see:
89
+ * // [Performance Analysis] Query degradation event queued for async processing | Job ID: abc-123 | ...
90
+ * //
91
+ * // 2. In consumer log (handlerAsyncDegradation), search for the same Job ID:
92
+ * // [Performance Analysis] Processing query degradation event | Job ID: abc-123 | ...
93
+ * //
94
+ * // 3. To find all related logs, search logs for: "Job ID: abc-123"
95
+ * // This will show both the queuing event and the processing event
96
+ * //
97
+ * // Example log flow:
98
+ * // WARN resolver: [Performance Analysis] Query degradation event queued... | Job ID: abc-123
99
+ * // WARN handlerAsyncDegradation: [Performance Analysis] Processing query degradation event | Job ID: abc-123
100
+ * // WARN handlerAsyncDegradation: SQL: SELECT ... | Time: 3514 ms
101
+ * ```
102
+ */
103
+ export async function printDegradationQueriesConsumer(
104
+ forgeSQLORM: ForgeSqlOperation,
105
+ event: AsyncEvent,
106
+ ): Promise<void> {
107
+ const body: AsyncEventPrintQuery = event.body as AsyncEventPrintQuery;
108
+ body.beginTime = new Date(body.beginTime);
109
+ // eslint-disable-next-line no-console
110
+ console.warn(
111
+ `[Performance Analysis] Processing query degradation event | Job ID: ${event.jobId} | Total DB time: ${body.totalDbExecutionTime}ms | Queries: ${body.statistics.length} | Started: ${body.beginTime.toISOString()}`,
112
+ );
113
+ await printDegradationQueries(forgeSQLORM, body);
114
+ }
@@ -6,6 +6,7 @@ import {
6
6
  customType,
7
7
  MySqlSelectBuilder,
8
8
  MySqlTable,
9
+ MySqlColumn,
9
10
  } from "drizzle-orm/mysql-core";
10
11
  import {
11
12
  MySqlSelectDynamic,
@@ -56,7 +57,6 @@ import { SQLWrapper } from "drizzle-orm/sql/sql";
56
57
  import type { MySqlQueryResultKind } from "drizzle-orm/mysql-core/session";
57
58
  import type { WithBuilder } from "drizzle-orm/mysql-core/subquery";
58
59
  import { WithSubquery } from "drizzle-orm/subquery";
59
- import { MySqlColumn } from "drizzle-orm/mysql-core";
60
60
  import { MetadataQueryOptions } from "../utils/metadataContextUtils";
61
61
 
62
62
  /**
@@ -1025,7 +1025,7 @@ export interface SchemaSqlForgeSql {
1025
1025
  * @returns {Promise<T[]>} A list of results as objects
1026
1026
  * @throws {Error} If the query execution fails
1027
1027
  */
1028
- executeRawSQL<T extends object | unknown>(query: string, params?: SqlParameters[]): Promise<T[]>;
1028
+ executeRawSQL<T>(query: string, params?: SqlParameters[]): Promise<T[]>;
1029
1029
 
1030
1030
  /**
1031
1031
  * Executes a raw SQL update query.
@@ -4,6 +4,7 @@ import {
4
4
  AnyMySqlSelectQueryBuilder,
5
5
  MySqlSelectDynamic,
6
6
  } from "drizzle-orm/mysql-core/query-builders/select.types";
7
+ import { SqlParameters } from "@forge/sql/out/sql-statement";
7
8
 
8
9
  /**
9
10
  * Class implementing SQL select operations for ForgeSQL ORM.
@@ -55,7 +56,7 @@ export class ForgeSQLSelectOperations implements SchemaSqlForgeSql {
55
56
  * @param {SqlParameters[]} [params] - Optional SQL parameters
56
57
  * @returns {Promise<T[]>} A list of results as objects
57
58
  */
58
- async executeRawSQL<T extends object | unknown>(query: string, params?: unknown[]): Promise<T[]> {
59
+ async executeRawSQL<T>(query: string, params?: SqlParameters[]): Promise<T[]> {
59
60
  if (this.options.logRawSqlQuery) {
60
61
  const paramsStr = params ? `, with params: ${JSON.stringify(params)}` : "";
61
62
  // eslint-disable-next-line no-console