@m2k-5f/pgtx 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +134 -0
  2. package/dist/clauses/array.clause.d.ts +10 -0
  3. package/dist/clauses/array.clause.d.ts.map +1 -0
  4. package/dist/clauses/array.clause.js +34 -0
  5. package/dist/clauses/base.clause.d.ts +5 -0
  6. package/dist/clauses/base.clause.d.ts.map +1 -0
  7. package/dist/clauses/base.clause.js +6 -0
  8. package/dist/clauses/fragment.clause.d.ts +10 -0
  9. package/dist/clauses/fragment.clause.d.ts.map +1 -0
  10. package/dist/clauses/fragment.clause.js +20 -0
  11. package/dist/clauses/iden.caluse.d.ts +9 -0
  12. package/dist/clauses/iden.caluse.d.ts.map +1 -0
  13. package/dist/clauses/iden.caluse.js +20 -0
  14. package/dist/clauses/static.clause.d.ts +9 -0
  15. package/dist/clauses/static.clause.d.ts.map +1 -0
  16. package/dist/clauses/static.clause.js +18 -0
  17. package/dist/clauses/update.clause.d.ts +10 -0
  18. package/dist/clauses/update.clause.d.ts.map +1 -0
  19. package/dist/clauses/update.clause.js +26 -0
  20. package/dist/clauses/values.clause.d.ts +9 -0
  21. package/dist/clauses/values.clause.d.ts.map +1 -0
  22. package/dist/clauses/values.clause.js +27 -0
  23. package/dist/connection.d.ts +50 -0
  24. package/dist/connection.d.ts.map +1 -0
  25. package/dist/connection.js +101 -0
  26. package/dist/index.d.ts +92 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +94 -0
  29. package/dist/pool.d.ts +59 -0
  30. package/dist/pool.d.ts.map +1 -0
  31. package/dist/pool.js +112 -0
  32. package/dist/query.cacher.d.ts +6 -0
  33. package/dist/query.cacher.d.ts.map +1 -0
  34. package/dist/query.cacher.js +23 -0
  35. package/dist/transaction.d.ts +40 -0
  36. package/dist/transaction.d.ts.map +1 -0
  37. package/dist/transaction.js +71 -0
  38. package/dist/types.d.ts +8 -0
  39. package/dist/types.d.ts.map +1 -0
  40. package/dist/types.js +2 -0
  41. package/dist/utils.d.ts +7 -0
  42. package/dist/utils.d.ts.map +1 -0
  43. package/dist/utils.js +27 -0
  44. package/package.json +25 -0
package/README.md ADDED
@@ -0,0 +1,134 @@
1
+ # Pgtx 🚀
2
+
3
+ A lightweight, **high-performance** SQL query builder for `node-postgres` (pg).
4
+ Experience ORM-like convenience (auto-inserts, updates, recursive fragments) with the transparency and speed of raw SQL.
5
+
6
+ ---
7
+
8
+ ## 🔥 Why Pgtx?
9
+
10
+ * **Zero-Cost Abstraction**: Only ~2% overhead compared to raw `pg.query`.
11
+ * **Structural Caching**: Uses `WeakMap` to cache SQL templates. Static parts are parsed only once.
12
+ * **Explicit Prepared Statements**: Create and reuse prepared statements with type safety
13
+ * **True Recursion**: Nest `sql.fragment` anywhere. Argument numbering ($1, $2) is managed automatically across all nesting levels.
14
+ * **No Magic**: You write SQL, `Pgtx` handles the tedious parts (placeholders, identifiers, bulk inserts).
15
+ * **ACID Transactions**: Reliable transaction management with automatic rollback on errors.
16
+
17
+ ---
18
+
19
+ ## 🚀 Quick Start
20
+
21
+ ```typescript
22
+ import { sql, Pool } from 'pgtx';
23
+
24
+ const pool = new Pool({ /* pg.PoolConfig */ })
25
+
26
+ // Type-safe query with automatic placeholder ($1)
27
+ const [user] = await pool.query<User>`SELECT * FROM users WHERE id = ${1}`
28
+ ```
29
+
30
+ ## ⚡ Performance Benchmark
31
+
32
+ The following results were measured during sequential execution of 10,000 complex `UPSERT` queries.
33
+
34
+
35
+ | Tool | RPS | Avg. Query Time | Performance Overhead |
36
+ | :--- | :---: | :---: | :---: |
37
+ | **Native `pg.query`** | **~194** | **5.15 ms** | **0% (Baseline)** |
38
+ | **Pgtx** | **~190** | **5.27 ms** | **~2.1%** |
39
+ | Typical Node.js ORM | **~58** | 12.5+ ms | > 150% |
40
+
41
+
42
+ ## 📖 Feature Guide
43
+
44
+ ### 1. Recursive Fragments (sql.fragment)
45
+ Combine multiple SQL pieces. Perfect for dynamic filters or subqueries.
46
+
47
+ ```typescript
48
+ const filter = sql.fragment`status = ${'active'} AND age > ${21}`
49
+ const roleSub = sql.fragment`(SELECT id FROM roles WHERE name = ${'admin'})`
50
+
51
+ await pool.query`
52
+ INSERT INTO users (name, role_id)
53
+ VALUES (${'Ivan'}, (${roleSub}))
54
+ WHERE ${filter}
55
+ `
56
+ // SQL: INSERT INTO users ... VALUES ($1, (SELECT id FROM roles WHERE name = $2)) WHERE status = $3 AND age > $4
57
+ ```
58
+
59
+ ## 2. Transactions
60
+ Automatic BEGIN, COMMIT, and ROLLBACK. Use savepoint for nested logic.
61
+
62
+ ```typescript
63
+ await pool.begin(async (tx) => {
64
+ await tx.query`UPDATE accounts SET balance = balance - 100 WHERE id = 1`;
65
+
66
+ // Nested transaction (Savepoint)
67
+ await tx.savepoint('inventory', async (stx) => {
68
+ await stx.query`UPDATE stock SET count = count - 1 WHERE item_id = ${42}`;
69
+ if (outOfStock) throw new Error(); // Only 'inventory' rolls back
70
+ });
71
+ });
72
+ // Main transaction commits or rolls back based on callback success
73
+ ```
74
+
75
+ ## 3. Bulk Inserts (sql.insert)
76
+ Automatically extracts columns from the first object. Supports single objects and arrays.
77
+
78
+ ```typescript
79
+ const users = [
80
+ { name: 'Alice', email: 'alice@test.com' },
81
+ { name: 'Bob', email: 'bob@test.com' }
82
+ ]
83
+
84
+ await pool.query`INSERT INTO users ${sql.insert(users)}`
85
+ // SQL: INSERT INTO users (name, email) VALUES ($1, $2), ($3, $4)
86
+ ```
87
+
88
+ ## 4. Prepared Statements
89
+ Pre-parse SQL on the database server for maximum performance in hot loops.
90
+
91
+ ```typescript
92
+ const stmt = await pool.prepare<User>("get_user_by_email", 'SELECT * FROM users WHERE email = ?')
93
+ // `Pgtx` automatically maps standard `?` placeholders to native `$1, $2` indexes.
94
+
95
+ const users = await stmt.execute('test@example.com')
96
+ // Statements created via pool.prepare are lazily initialized on each connection upon first use,
97
+ // gradually "warming up" the entire pool for peak performance.
98
+ ```
99
+
100
+ ### 5. Smart Lists (sql.array)
101
+ The ultimate tool for dynamic lists. Works for IN clauses, column lists, or joined conditions.
102
+
103
+ ```typescript
104
+ // 1. Classic IN clause
105
+ const ids = [10, 20]
106
+ await pool.query`SELECT * FROM users WHERE id IN (${sql.array(ids)})`
107
+ // SQL: SELECT * FROM users WHERE id IN ($1, $2)
108
+
109
+ // 2. Dynamic column list
110
+ const cols = [sql.ident('id'), sql.ident('name')]
111
+ await pool.query`SELECT ${sql.array(cols)} FROM users`
112
+ // SQL: SELECT "id", "name" FROM users
113
+
114
+ // 3. Dynamic WHERE conditions
115
+ const conds = [sql.fragment`active = true`, sql.fragment`age > ${18}`]
116
+ await pool.query`SELECT * FROM users WHERE ${sql.array(conds, ' AND ')}`
117
+ // SQL: SELECT * FROM users WHERE active = true AND age > $1
118
+ ```
119
+
120
+ ## 6. Dynamic Updates (sql.update)
121
+ Easily generate SET clauses from plain JavaScript objects.
122
+
123
+ ```typescript
124
+ const data = { status: 'pro', last_login: new Date() }
125
+
126
+ await pool.query`UPDATE users SET ${sql.update(data)} WHERE id = ${1}`
127
+ // SQL: UPDATE users SET status = $1, last_login = $2 WHERE id = $3
128
+ ```
129
+
130
+
131
+ ## 🛡️ Security
132
+ * **SQL Injection**: Automatically uses native placeholders ($1, $2) for all values.
133
+ * **Identifiers**: `sql.ident` safely escapes table and column names using double quotes.
134
+ * **Connection Leaks**: `pool.query` uses try...finally internally to ensure connections are always returned to the pool.
@@ -0,0 +1,10 @@
1
+ import { Clause } from "../clauses/base.clause";
2
+ import { CompiledSqlQuery } from "../utils";
3
+ export declare class ArrayClause extends Clause {
4
+ private readonly array;
5
+ private readonly separator;
6
+ constructor(array: any[], separator?: string);
7
+ map(argCounter: number): CompiledSqlQuery;
8
+ }
9
+ export declare function arrayClause(array: any[], separator?: string): ArrayClause;
10
+ //# sourceMappingURL=array.clause.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"array.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/array.clause.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,qBAAa,WAAY,SAAQ,MAAM;IAE/B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBADT,KAAK,EAAE,GAAG,EAAE,EACZ,SAAS,GAAE,MAAa;IAGpC,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB;CAmBrD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,SAAS,GAAE,MAAa,GAAG,WAAW,CAE/E"}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ArrayClause = void 0;
4
+ exports.arrayClause = arrayClause;
5
+ const base_clause_1 = require("../clauses/base.clause");
6
+ class ArrayClause extends base_clause_1.Clause {
7
+ constructor(array, separator = ", ") {
8
+ super();
9
+ this.array = array;
10
+ this.separator = separator;
11
+ }
12
+ map(argCounter) {
13
+ if (this.array.length === 0)
14
+ return { text: 'NULL', args: [], argCounter };
15
+ const args = [];
16
+ const text = `${this.array.map(value => {
17
+ if (value instanceof base_clause_1.Clause) {
18
+ const result = value.map(argCounter);
19
+ args.push(...result.args);
20
+ argCounter = result.argCounter;
21
+ return result.text;
22
+ }
23
+ else {
24
+ args.push(value);
25
+ return `$${argCounter++}`;
26
+ }
27
+ }).join(this.separator)}`;
28
+ return { text, args, argCounter };
29
+ }
30
+ }
31
+ exports.ArrayClause = ArrayClause;
32
+ function arrayClause(array, separator = ", ") {
33
+ return new ArrayClause(array, separator);
34
+ }
@@ -0,0 +1,5 @@
1
+ import { CompiledSqlQuery } from "./../utils";
2
+ export declare abstract class Clause {
3
+ abstract map(argCounter: number): CompiledSqlQuery;
4
+ }
5
+ //# sourceMappingURL=base.clause.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/base.clause.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,8BAAsB,MAAM;IACxB,QAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB;CACrD"}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Clause = void 0;
4
+ class Clause {
5
+ }
6
+ exports.Clause = Clause;
@@ -0,0 +1,10 @@
1
+ import { Clause } from "./base.clause";
2
+ import { CompiledSqlQuery } from "../utils";
3
+ export declare class FragmentClause extends Clause {
4
+ readonly strings: TemplateStringsArray;
5
+ readonly values: any[];
6
+ constructor(strings: TemplateStringsArray, values: any[]);
7
+ map(currentArgCounter: number): CompiledSqlQuery;
8
+ }
9
+ export declare function fragmentClause(strings: TemplateStringsArray, ...values: any[]): FragmentClause;
10
+ //# sourceMappingURL=fragment.clause.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fragment.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/fragment.clause.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAI,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAsB,MAAM,UAAU,CAAC;AAEhE,qBAAa,cAAe,SAAQ,MAAM;IAElC,QAAQ,CAAC,OAAO,EAAE,oBAAoB;IACtC,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;gBADb,OAAO,EAAE,oBAAoB,EAC7B,MAAM,EAAE,GAAG,EAAE;IAGjB,GAAG,CAAC,iBAAiB,EAAE,MAAM,GAAG,gBAAgB;CAG5D;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,kBAE7E"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FragmentClause = void 0;
4
+ exports.fragmentClause = fragmentClause;
5
+ const base_clause_1 = require("./base.clause");
6
+ const utils_1 = require("../utils");
7
+ class FragmentClause extends base_clause_1.Clause {
8
+ constructor(strings, values) {
9
+ super();
10
+ this.strings = strings;
11
+ this.values = values;
12
+ }
13
+ map(currentArgCounter) {
14
+ return (0, utils_1.compileSqlTemplate)(this.strings, this.values, currentArgCounter);
15
+ }
16
+ }
17
+ exports.FragmentClause = FragmentClause;
18
+ function fragmentClause(strings, ...values) {
19
+ return new FragmentClause(strings, values);
20
+ }
@@ -0,0 +1,9 @@
1
+ import { Clause } from "./base.clause";
2
+ import { CompiledSqlQuery } from "../utils";
3
+ export declare class IdentifierClause<T extends string> extends Clause {
4
+ readonly value: T;
5
+ constructor(value: T);
6
+ map(argCounter: number): CompiledSqlQuery;
7
+ }
8
+ export declare function identClause<T extends string>(identificator: T): IdentifierClause<T>;
9
+ //# sourceMappingURL=iden.caluse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iden.caluse.d.ts","sourceRoot":"","sources":["../../src/clauses/iden.caluse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,qBAAa,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAE,SAAQ,MAAM;IAEtD,QAAQ,CAAC,KAAK,EAAE,CAAC;gBAAR,KAAK,EAAE,CAAC;IAGZ,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB;CAMrD;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,aAAa,EAAE,CAAC,uBAE7D"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IdentifierClause = void 0;
4
+ exports.identClause = identClause;
5
+ const base_clause_1 = require("./base.clause");
6
+ class IdentifierClause extends base_clause_1.Clause {
7
+ constructor(value) {
8
+ super();
9
+ this.value = value;
10
+ }
11
+ map(argCounter) {
12
+ const text = `"${this.value}"`;
13
+ const args = [];
14
+ return { text, args, argCounter };
15
+ }
16
+ }
17
+ exports.IdentifierClause = IdentifierClause;
18
+ function identClause(identificator) {
19
+ return new IdentifierClause(identificator);
20
+ }
@@ -0,0 +1,9 @@
1
+ import { Clause } from "./base.clause";
2
+ import { CompiledSqlQuery } from "../utils";
3
+ export declare class StaticClause<T extends string> extends Clause {
4
+ readonly value: T;
5
+ constructor(value: T);
6
+ map(argCounter: number): CompiledSqlQuery;
7
+ }
8
+ export declare function staticClause<T extends string>(value: T): StaticClause<T>;
9
+ //# sourceMappingURL=static.clause.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/static.clause.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAC,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,qBAAa,YAAY,CAAC,CAAC,SAAS,MAAM,CAAE,SAAQ,MAAM;IAElD,QAAQ,CAAC,KAAK,EAAE,CAAC;gBAAR,KAAK,EAAE,CAAC;IAKZ,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB;CAGrD;AAGD,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAExE"}
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StaticClause = void 0;
4
+ exports.staticClause = staticClause;
5
+ const base_clause_1 = require("./base.clause");
6
+ class StaticClause extends base_clause_1.Clause {
7
+ constructor(value) {
8
+ super();
9
+ this.value = value;
10
+ }
11
+ map(argCounter) {
12
+ return { text: this.value, args: [], argCounter };
13
+ }
14
+ }
15
+ exports.StaticClause = StaticClause;
16
+ function staticClause(value) {
17
+ return new StaticClause(value);
18
+ }
@@ -0,0 +1,10 @@
1
+ import { Clause } from "./base.clause";
2
+ import { CompiledSqlQuery } from "../utils";
3
+ export declare class UpdateClause<T extends Record<string, any>> extends Clause {
4
+ readonly updateMap: T;
5
+ readonly columns?: (keyof T)[] | undefined;
6
+ constructor(updateMap: T, columns?: (keyof T)[] | undefined);
7
+ map(argCounter: number): CompiledSqlQuery;
8
+ }
9
+ export declare function updateClause<T extends Record<string, any>>(object: T, ...updateColumns: (keyof T)[]): UpdateClause<T>;
10
+ //# sourceMappingURL=update.clause.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/update.clause.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,qBAAa,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAE,SAAQ,MAAM;IAE/D,QAAQ,CAAC,SAAS,EAAE,CAAC;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE;gBADrB,SAAS,EAAE,CAAC,EACZ,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,YAAA;IAGzB,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB;CAerD;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,aAAa,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC,CAErH"}
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UpdateClause = void 0;
4
+ exports.updateClause = updateClause;
5
+ const base_clause_1 = require("./base.clause");
6
+ class UpdateClause extends base_clause_1.Clause {
7
+ constructor(updateMap, columns) {
8
+ super();
9
+ this.updateMap = updateMap;
10
+ this.columns = columns;
11
+ }
12
+ map(argCounter) {
13
+ const columns = this.columns || Object.keys(this.updateMap);
14
+ const args = [];
15
+ const text = columns
16
+ .map((key) => {
17
+ args.push(this.updateMap[key]);
18
+ return `${key.toString()} = $${argCounter++}`;
19
+ }).join(', ');
20
+ return { text, args, argCounter };
21
+ }
22
+ }
23
+ exports.UpdateClause = UpdateClause;
24
+ function updateClause(object, ...updateColumns) {
25
+ return new UpdateClause(object, updateColumns.length > 0 ? updateColumns : undefined);
26
+ }
@@ -0,0 +1,9 @@
1
+ import { Clause } from "./base.clause";
2
+ import { CompiledSqlQuery } from "../utils";
3
+ export declare class InsertClause extends Clause {
4
+ readonly inserts: Record<string, any>[];
5
+ constructor(inserts: Record<string, any>[]);
6
+ map(argCounter: number): CompiledSqlQuery;
7
+ }
8
+ export declare function valueClause<T extends Record<string, any>>(...objects: [T, ...NoInfer<T>[]]): InsertClause;
9
+ //# sourceMappingURL=values.clause.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"values.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/values.clause.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,qBAAa,YAAa,SAAQ,MAAM;IAEhC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE;gBAA9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE;IAGlC,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB;CAgBrD;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,gBAE1F"}
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InsertClause = void 0;
4
+ exports.valueClause = valueClause;
5
+ const base_clause_1 = require("./base.clause");
6
+ class InsertClause extends base_clause_1.Clause {
7
+ constructor(inserts) {
8
+ super();
9
+ this.inserts = inserts;
10
+ }
11
+ map(argCounter) {
12
+ const columns = Object.keys(this.inserts[0]);
13
+ let text = `(${columns.join(', ')}) VALUES `;
14
+ let args = [];
15
+ text += `${this.inserts.map(values => {
16
+ return `(${Object.values(values).map(value => {
17
+ args.push(value);
18
+ return `$${argCounter++}`;
19
+ }).join(", ")})`;
20
+ }).join(", ")}`;
21
+ return { text, args, argCounter };
22
+ }
23
+ }
24
+ exports.InsertClause = InsertClause;
25
+ function valueClause(...objects) {
26
+ return new InsertClause(objects);
27
+ }
@@ -0,0 +1,50 @@
1
+ import { PoolClient, QueryResultRow } from "pg";
2
+ import { PreparedStatement } from "./types";
3
+ import { Transaction } from "./transaction";
4
+ /**
5
+ * Represents a single dedicated connection to the database.
6
+ * Always remember to call `.release()` when finished.
7
+ */
8
+ export declare class Connection {
9
+ private readonly client;
10
+ private isReleased;
11
+ constructor(client: PoolClient);
12
+ /**
13
+ * Checks if the connection is still open and not returned to the pool.
14
+ */
15
+ get isActive(): boolean;
16
+ /**
17
+ * Creates a prepared statement on this specific connection.
18
+ * Maps '?' placeholders to native PostgreSQL '$1, $2' indexes.
19
+ *
20
+ * @example
21
+ * const stmt = await conn.prepare('get_user', 'SELECT * FROM users WHERE id = ?');
22
+ * const rows = await stmt.execute(1);
23
+ */
24
+ prepare<TResult extends QueryResultRow = any, TParams extends any[] = any[]>(name: string, sqlTemplate: string): Promise<PreparedStatement<TResult, TParams>>;
25
+ /**
26
+ * Releases the connection back to the pool.
27
+ * The connection cannot be used after this call.
28
+ */
29
+ release(): void;
30
+ private checkActive;
31
+ /**
32
+ * Executes a tagged SQL query using structural caching.
33
+ * Supports recursive fragments and clauses.
34
+ *
35
+ * @example
36
+ * const users = await conn.query`SELECT * FROM users WHERE id = ${1}`;
37
+ */
38
+ query<T extends QueryResultRow = any>(strings: TemplateStringsArray, ...values: any[]): Promise<T[]>;
39
+ /**
40
+ * Starts a managed transaction on this connection.
41
+ * Handles automatic COMMIT on success or ROLLBACK on error.
42
+ *
43
+ * @example
44
+ * await conn.begin(async (tx) => {
45
+ * await tx.query`INSERT INTO logs ...`;
46
+ * });
47
+ */
48
+ begin<T>(callback: (tx: Transaction) => Promise<T>): Promise<T>;
49
+ }
50
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,IAAI,CAAA;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAK3C;;;GAGG;AACH,qBAAa,UAAU;IAIf,OAAO,CAAC,QAAQ,CAAC,MAAM;IAH3B,OAAO,CAAC,UAAU,CAAiB;gBAGd,MAAM,EAAE,UAAU;IAGvC;;OAEG;IACH,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED;;;;;;;OAOG;IACU,OAAO,CAAC,OAAO,SAAS,cAAc,GAAG,GAAG,EAAE,OAAO,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,EACpF,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAoB/C;;;OAGG;IACI,OAAO,IAAI,IAAI;IAMtB,OAAO,CAAC,WAAW;IAMnB;;;;;;OAMG;IACU,KAAK,CAAC,CAAC,SAAS,cAAc,GAAG,GAAG,EAAE,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAUjH;;;;;;;;OAQG;IACU,KAAK,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAmB/E"}
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Connection = void 0;
4
+ const transaction_1 = require("./transaction");
5
+ const query_cacher_1 = require("./query.cacher");
6
+ const cacher = new query_cacher_1.QueryCacher();
7
+ /**
8
+ * Represents a single dedicated connection to the database.
9
+ * Always remember to call `.release()` when finished.
10
+ */
11
+ class Connection {
12
+ constructor(client) {
13
+ this.client = client;
14
+ this.isReleased = false;
15
+ }
16
+ /**
17
+ * Checks if the connection is still open and not returned to the pool.
18
+ */
19
+ get isActive() {
20
+ return !this.isReleased;
21
+ }
22
+ /**
23
+ * Creates a prepared statement on this specific connection.
24
+ * Maps '?' placeholders to native PostgreSQL '$1, $2' indexes.
25
+ *
26
+ * @example
27
+ * const stmt = await conn.prepare('get_user', 'SELECT * FROM users WHERE id = ?');
28
+ * const rows = await stmt.execute(1);
29
+ */
30
+ async prepare(name, sqlTemplate) {
31
+ let index = 1;
32
+ const text = sqlTemplate.replace(/\?/g, () => `$${index++}`);
33
+ return {
34
+ text: text,
35
+ name: name,
36
+ execute: async (...args) => {
37
+ this.checkActive();
38
+ const result = await this.client.query({
39
+ name,
40
+ text,
41
+ values: args
42
+ });
43
+ return result.rows;
44
+ }
45
+ };
46
+ }
47
+ /**
48
+ * Releases the connection back to the pool.
49
+ * The connection cannot be used after this call.
50
+ */
51
+ release() {
52
+ if (this.isReleased)
53
+ return;
54
+ this.isReleased = true;
55
+ this.client.release();
56
+ }
57
+ checkActive() {
58
+ if (this.isReleased) {
59
+ throw new Error("Connection has been released");
60
+ }
61
+ }
62
+ /**
63
+ * Executes a tagged SQL query using structural caching.
64
+ * Supports recursive fragments and clauses.
65
+ *
66
+ * @example
67
+ * const users = await conn.query`SELECT * FROM users WHERE id = ${1}`;
68
+ */
69
+ async query(strings, ...values) {
70
+ this.checkActive();
71
+ const { text, args } = cacher.cachedBuild(strings, values, 1);
72
+ const result = await this.client.query(text, args);
73
+ return result.rows;
74
+ }
75
+ /**
76
+ * Starts a managed transaction on this connection.
77
+ * Handles automatic COMMIT on success or ROLLBACK on error.
78
+ *
79
+ * @example
80
+ * await conn.begin(async (tx) => {
81
+ * await tx.query`INSERT INTO logs ...`;
82
+ * });
83
+ */
84
+ async begin(callback) {
85
+ this.checkActive();
86
+ const tx = new transaction_1.Transaction(this);
87
+ try {
88
+ await tx.query `BEGIN`;
89
+ const result = await callback(tx);
90
+ if (tx.isActive)
91
+ await tx.commit();
92
+ return result;
93
+ }
94
+ catch (err) {
95
+ if (tx.isActive)
96
+ await tx.rollback();
97
+ throw err;
98
+ }
99
+ }
100
+ }
101
+ exports.Connection = Connection;
@@ -0,0 +1,92 @@
1
+ import { arrayClause } from "./clauses/array.clause";
2
+ import { fragmentClause } from "./clauses/fragment.clause";
3
+ import { identClause } from "./clauses/iden.caluse";
4
+ import { Pool as PgtxPool } from "./pool";
5
+ import { staticClause } from "./clauses/static.clause";
6
+ import { updateClause } from "./clauses/update.clause";
7
+ import { valueClause } from "./clauses/values.clause";
8
+ /**
9
+ * Core SQL tagging utility for Pgtx.
10
+ * Provides type-safe helpers for building dynamic queries with recursive support.
11
+ */
12
+ export declare const sql: {
13
+ /**
14
+ * Creates a VALUES clause for INSERT queries.
15
+ * Supports single objects and arrays of objects.
16
+ *
17
+ * @example
18
+ * sql.insert({ name: 'Ivan', age: 25 })
19
+ * // Result: (name, age) VALUES ($1, $2)
20
+ *
21
+ * @example
22
+ * sql.insert([{ id: 1 }, { id: 2 }])
23
+ * // Result: (id) VALUES ($1), ($2)
24
+ */
25
+ insert: typeof valueClause;
26
+ /**
27
+ * Generates a SET clause for UPDATE queries from a JavaScript object.
28
+ *
29
+ * @example
30
+ * sql.update({ status: 'active', updated_at: new Date() })
31
+ * // Result: status = $1, updated_at = $2
32
+ */
33
+ update: typeof updateClause;
34
+ /**
35
+ * Safely escapes SQL identifiers (table or column names) using double quotes.
36
+ *
37
+ * @example
38
+ * sql.ident('users')
39
+ * // Result: "users"
40
+ *
41
+ * @example
42
+ * sql.ident('table.column')
43
+ * // Result: "table.column"
44
+ */
45
+ ident: typeof identClause;
46
+ /**
47
+ * Injects raw, unescaped SQL strings.
48
+ * ⚠️ Use with caution to prevent SQL injection!
49
+ *
50
+ * @example
51
+ * sql.literal('DESC')
52
+ * // Result: DESC
53
+ */
54
+ literal: typeof staticClause;
55
+ /**
56
+ * Creates a reusable, recursive SQL fragment.
57
+ * Fragments can be nested within each other; argument numbering is handled automatically.
58
+ *
59
+ * @example
60
+ * const filter = sql.fragment`age > ${18}`;
61
+ * sql`SELECT * FROM users WHERE ${filter} AND status = ${'active'}`
62
+ * // Result: SELECT * FROM users WHERE age > $1 AND status = $2
63
+ */
64
+ fragment: typeof fragmentClause;
65
+ /**
66
+ * Formats an array for dynamic lists (IN clauses, column lists, or joined conditions).
67
+ * Supports recursive Clauses (fragments, idents) within the array.
68
+ *
69
+ * @example
70
+ * // Case A: IN clause (manual brackets)
71
+ * sql`WHERE id IN (${sql.array([1, 2])})`
72
+ * // Result: WHERE id IN ($1, $2)
73
+ *
74
+ * @example
75
+ * // Case B: Dynamic WHERE conditions
76
+ * const conds = [sql.fragment`a = 1`, sql.fragment`b = ${2}`];
77
+ * sql`WHERE ${sql.array(conds, ' AND ')}`
78
+ * // Result: WHERE a = 1 AND b = $1
79
+ *
80
+ * @example
81
+ * // Case C: Column list
82
+ * sql`SELECT ${sql.array([sql.ident('id'), sql.ident('name')])}`
83
+ * // Result: SELECT "id", "name"
84
+ */
85
+ array: typeof arrayClause;
86
+ };
87
+ /**
88
+ * Main Pgtx Connection Pool.
89
+ * Manages connections, transactions (including SAVEPOINTs), and prepared statements.
90
+ */
91
+ export declare const Pool: typeof PgtxPool;
92
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD;;;GAGG;AACH,eAAO,MAAM,GAAG;IACZ;;;;;;;;;;;OAWG;;IAGH;;;;;;OAMG;;IAGH;;;;;;;;;;OAUG;;IAGH;;;;;;;OAOG;;IAGH;;;;;;;;OAQG;;IAGH;;;;;;;;;;;;;;;;;;;OAmBG;;CAEN,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,IAAI,iBAAW,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Pool = exports.sql = void 0;
4
+ const array_clause_1 = require("./clauses/array.clause");
5
+ const fragment_clause_1 = require("./clauses/fragment.clause");
6
+ const iden_caluse_1 = require("./clauses/iden.caluse");
7
+ const pool_1 = require("./pool");
8
+ const static_clause_1 = require("./clauses/static.clause");
9
+ const update_clause_1 = require("./clauses/update.clause");
10
+ const values_clause_1 = require("./clauses/values.clause");
11
+ /**
12
+ * Core SQL tagging utility for Pgtx.
13
+ * Provides type-safe helpers for building dynamic queries with recursive support.
14
+ */
15
+ exports.sql = {
16
+ /**
17
+ * Creates a VALUES clause for INSERT queries.
18
+ * Supports single objects and arrays of objects.
19
+ *
20
+ * @example
21
+ * sql.insert({ name: 'Ivan', age: 25 })
22
+ * // Result: (name, age) VALUES ($1, $2)
23
+ *
24
+ * @example
25
+ * sql.insert([{ id: 1 }, { id: 2 }])
26
+ * // Result: (id) VALUES ($1), ($2)
27
+ */
28
+ insert: values_clause_1.valueClause,
29
+ /**
30
+ * Generates a SET clause for UPDATE queries from a JavaScript object.
31
+ *
32
+ * @example
33
+ * sql.update({ status: 'active', updated_at: new Date() })
34
+ * // Result: status = $1, updated_at = $2
35
+ */
36
+ update: update_clause_1.updateClause,
37
+ /**
38
+ * Safely escapes SQL identifiers (table or column names) using double quotes.
39
+ *
40
+ * @example
41
+ * sql.ident('users')
42
+ * // Result: "users"
43
+ *
44
+ * @example
45
+ * sql.ident('table.column')
46
+ * // Result: "table.column"
47
+ */
48
+ ident: iden_caluse_1.identClause,
49
+ /**
50
+ * Injects raw, unescaped SQL strings.
51
+ * ⚠️ Use with caution to prevent SQL injection!
52
+ *
53
+ * @example
54
+ * sql.literal('DESC')
55
+ * // Result: DESC
56
+ */
57
+ literal: static_clause_1.staticClause,
58
+ /**
59
+ * Creates a reusable, recursive SQL fragment.
60
+ * Fragments can be nested within each other; argument numbering is handled automatically.
61
+ *
62
+ * @example
63
+ * const filter = sql.fragment`age > ${18}`;
64
+ * sql`SELECT * FROM users WHERE ${filter} AND status = ${'active'}`
65
+ * // Result: SELECT * FROM users WHERE age > $1 AND status = $2
66
+ */
67
+ fragment: fragment_clause_1.fragmentClause,
68
+ /**
69
+ * Formats an array for dynamic lists (IN clauses, column lists, or joined conditions).
70
+ * Supports recursive Clauses (fragments, idents) within the array.
71
+ *
72
+ * @example
73
+ * // Case A: IN clause (manual brackets)
74
+ * sql`WHERE id IN (${sql.array([1, 2])})`
75
+ * // Result: WHERE id IN ($1, $2)
76
+ *
77
+ * @example
78
+ * // Case B: Dynamic WHERE conditions
79
+ * const conds = [sql.fragment`a = 1`, sql.fragment`b = ${2}`];
80
+ * sql`WHERE ${sql.array(conds, ' AND ')}`
81
+ * // Result: WHERE a = 1 AND b = $1
82
+ *
83
+ * @example
84
+ * // Case C: Column list
85
+ * sql`SELECT ${sql.array([sql.ident('id'), sql.ident('name')])}`
86
+ * // Result: SELECT "id", "name"
87
+ */
88
+ array: array_clause_1.arrayClause,
89
+ };
90
+ /**
91
+ * Main Pgtx Connection Pool.
92
+ * Manages connections, transactions (including SAVEPOINTs), and prepared statements.
93
+ */
94
+ exports.Pool = pool_1.Pool;
package/dist/pool.d.ts ADDED
@@ -0,0 +1,59 @@
1
+ import { QueryResultRow } from "pg";
2
+ import { PoolConfig, PreparedStatement } from "./types";
3
+ import { Connection } from "./connection";
4
+ import { Transaction } from "./transaction";
5
+ /**
6
+ * The main entry point for Pgtx.
7
+ * Manages a connection pool and provides high-level API for queries and transactions.
8
+ */
9
+ export declare class Pool {
10
+ private pool;
11
+ constructor(config: PoolConfig);
12
+ /**
13
+ * Executes a one-off query.
14
+ * Automatically acquires and releases a connection from the pool.
15
+ *
16
+ * @example
17
+ * const users = await pool.query<User>`SELECT * FROM users WHERE id = ${1}`;
18
+ */
19
+ query<T extends QueryResultRow>(strings: TemplateStringsArray, ...values: any[]): Promise<T[]>;
20
+ /**
21
+ * Creates a reusable prepared statement.
22
+ * When executed via `stmt.execute()`, it automatically manages its own connection.
23
+ * Maps '?' placeholders to native PostgreSQL '$1, $2' indexes.
24
+ *
25
+ * @example
26
+ * const stmt = await pool.prepare<User, [string]>('get_user', 'SELECT * FROM users WHERE email = ?');
27
+ * const users = await stmt.execute('test@example.com');
28
+ */
29
+ prepare<TResult extends QueryResultRow = any, TParams extends any[] = any[]>(name: string, sqlTemplate: string): Promise<PreparedStatement<TResult, TParams>>;
30
+ /**
31
+ * Acquires a dedicated connection from the pool.
32
+ * **Note:** You must call `connection.release()` manually when finished.
33
+ */
34
+ acquire(): Promise<Connection>;
35
+ /**
36
+ * Starts a managed transaction.
37
+ * Automatically acquires a connection and handles BEGIN/COMMIT/ROLLBACK.
38
+ *
39
+ * @example
40
+ * const result = await pool.begin(async (tx) => {
41
+ * await tx.query`INSERT INTO accounts ...`;
42
+ * return "success";
43
+ * });
44
+ */
45
+ begin<T>(callback: (tx: Transaction) => Promise<T>): Promise<T>;
46
+ /**
47
+ * Returns current pool utilization statistics.
48
+ */
49
+ get stats(): {
50
+ total: number;
51
+ idle: number;
52
+ waiting: number;
53
+ };
54
+ /**
55
+ * Shuts down the pool and closes all active connections.
56
+ */
57
+ close(): Promise<void>;
58
+ }
59
+ //# sourceMappingURL=pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,cAAc,EAAC,MAAM,IAAI,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAE3C;;;GAGG;AACH,qBAAa,IAAI;IACb,OAAO,CAAC,IAAI,CAAQ;gBAGhB,MAAM,EAAE,UAAU;IAKtB;;;;;;OAMG;IACU,KAAK,CAAC,CAAC,SAAS,cAAc,EAAE,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE;IAc5F;;;;;;;;OAQG;IACU,OAAO,CAAC,OAAO,SAAS,cAAc,GAAG,GAAG,EAAE,OAAO,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,EACpF,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAyB/C;;;OAGG;IACU,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC;IAK3C;;;;;;;;;OASG;IACU,KAAK,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAc5E;;OAEG;IACH,IAAW,KAAK;;;;MAMf;IAED;;OAEG;IACI,KAAK;CAGf"}
package/dist/pool.js ADDED
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Pool = void 0;
4
+ const pg_1 = require("pg");
5
+ const connection_1 = require("./connection");
6
+ /**
7
+ * The main entry point for Pgtx.
8
+ * Manages a connection pool and provides high-level API for queries and transactions.
9
+ */
10
+ class Pool {
11
+ constructor(config) {
12
+ this.pool = new pg_1.Pool(config);
13
+ }
14
+ /**
15
+ * Executes a one-off query.
16
+ * Automatically acquires and releases a connection from the pool.
17
+ *
18
+ * @example
19
+ * const users = await pool.query<User>`SELECT * FROM users WHERE id = ${1}`;
20
+ */
21
+ async query(strings, ...values) {
22
+ const conn = await this.acquire();
23
+ try {
24
+ return await conn.query(strings, ...values);
25
+ }
26
+ catch (err) {
27
+ throw err;
28
+ }
29
+ finally {
30
+ conn.release();
31
+ }
32
+ }
33
+ /**
34
+ * Creates a reusable prepared statement.
35
+ * When executed via `stmt.execute()`, it automatically manages its own connection.
36
+ * Maps '?' placeholders to native PostgreSQL '$1, $2' indexes.
37
+ *
38
+ * @example
39
+ * const stmt = await pool.prepare<User, [string]>('get_user', 'SELECT * FROM users WHERE email = ?');
40
+ * const users = await stmt.execute('test@example.com');
41
+ */
42
+ async prepare(name, sqlTemplate) {
43
+ let index = 1;
44
+ const text = sqlTemplate.replace(/\?/g, () => `$${index++}`);
45
+ return {
46
+ text,
47
+ name,
48
+ execute: async (...args) => {
49
+ const conn = await this.acquire();
50
+ conn['checkActive']();
51
+ try {
52
+ const result = await conn['client'].query({
53
+ name,
54
+ text,
55
+ values: args
56
+ });
57
+ return result.rows;
58
+ }
59
+ finally {
60
+ conn.release();
61
+ }
62
+ }
63
+ };
64
+ }
65
+ /**
66
+ * Acquires a dedicated connection from the pool.
67
+ * **Note:** You must call `connection.release()` manually when finished.
68
+ */
69
+ async acquire() {
70
+ const client = await this.pool.connect();
71
+ return new connection_1.Connection(client);
72
+ }
73
+ /**
74
+ * Starts a managed transaction.
75
+ * Automatically acquires a connection and handles BEGIN/COMMIT/ROLLBACK.
76
+ *
77
+ * @example
78
+ * const result = await pool.begin(async (tx) => {
79
+ * await tx.query`INSERT INTO accounts ...`;
80
+ * return "success";
81
+ * });
82
+ */
83
+ async begin(callback) {
84
+ const conn = await this.acquire();
85
+ try {
86
+ return await conn.begin(callback);
87
+ }
88
+ catch (err) {
89
+ throw err;
90
+ }
91
+ finally {
92
+ conn.release();
93
+ }
94
+ }
95
+ /**
96
+ * Returns current pool utilization statistics.
97
+ */
98
+ get stats() {
99
+ return {
100
+ total: this.pool.totalCount,
101
+ idle: this.pool.idleCount,
102
+ waiting: this.pool.waitingCount
103
+ };
104
+ }
105
+ /**
106
+ * Shuts down the pool and closes all active connections.
107
+ */
108
+ close() {
109
+ return this.pool.end();
110
+ }
111
+ }
112
+ exports.Pool = Pool;
@@ -0,0 +1,6 @@
1
+ import { CompiledSqlQuery } from "./utils";
2
+ export declare class QueryCacher {
3
+ private readonly cache;
4
+ cachedBuild(strings: TemplateStringsArray, values: any[], argCounter: number): CompiledSqlQuery;
5
+ }
6
+ //# sourceMappingURL=query.cacher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.cacher.d.ts","sourceRoot":"","sources":["../src/query.cacher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAsB,MAAM,SAAS,CAAC;AAG/D,qBAAa,WAAW;IACpB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyE;IAExF,WAAW,CAAC,OAAO,EAAE,oBAAoB,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,gBAAgB;CAezG"}
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.QueryCacher = void 0;
4
+ const base_clause_1 = require("./clauses/base.clause");
5
+ const utils_1 = require("./utils");
6
+ class QueryCacher {
7
+ constructor() {
8
+ this.cache = new WeakMap();
9
+ }
10
+ cachedBuild(strings, values, argCounter) {
11
+ const cached = this.cache.get(strings);
12
+ if (cached?.isStatic) {
13
+ return { text: cached.text, args: values, argCounter };
14
+ }
15
+ const result = (0, utils_1.compileSqlTemplate)(strings, values, 1);
16
+ if (!cached) {
17
+ const isStatic = !values.some(value => value instanceof base_clause_1.Clause);
18
+ this.cache.set(strings, { text: result.text, isStatic });
19
+ }
20
+ return result;
21
+ }
22
+ }
23
+ exports.QueryCacher = QueryCacher;
@@ -0,0 +1,40 @@
1
+ import { QueryResultRow } from "pg";
2
+ import { Connection } from "./connection";
3
+ /**
4
+ * Represents an active SQL transaction.
5
+ * All queries are executed on a single dedicated connection.
6
+ */
7
+ export declare class Transaction {
8
+ readonly conn: Connection;
9
+ private isFinished;
10
+ constructor(conn: Connection);
11
+ /**
12
+ * Returns true if the transaction is still open (not committed or rolled back).
13
+ */
14
+ get isActive(): boolean;
15
+ private checkActive;
16
+ /**
17
+ * Commits the current transaction.
18
+ */
19
+ commit(): Promise<void>;
20
+ /**
21
+ * Rolls back the current transaction.
22
+ */
23
+ rollback(): Promise<void>;
24
+ /**
25
+ * Executes a query within the current transaction.
26
+ */
27
+ query<T extends QueryResultRow = any>(strings: TemplateStringsArray, ...values: any[]): Promise<T[]>;
28
+ /**
29
+ * Creates a sub-transaction using PostgreSQL SAVEPOINT.
30
+ * If the callback throws, only the actions within this savepoint are rolled back.
31
+ *
32
+ * @example
33
+ * await tx.savepoint('my_point', async (stx) => {
34
+ * await stx.query`INSERT ...`;
35
+ * if (error) throw new Error(); // Only this insert rolls back
36
+ * });
37
+ */
38
+ savepoint(name: string, callback: (tx: Transaction) => Promise<void>): Promise<void>;
39
+ }
40
+ //# sourceMappingURL=transaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,IAAI,CAAA;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAIzC;;;GAGG;AACH,qBAAa,WAAW;IAIhB,QAAQ,CAAC,IAAI,EAAE,UAAU;IAH7B,OAAO,CAAC,UAAU,CAAiB;gBAGtB,IAAI,EAAE,UAAU;IAG7B;;OAEG;IACH,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED,OAAO,CAAC,WAAW;IAMnB;;OAEG;IACU,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAOpC;;OAEG;IACU,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAOtC;;OAEG;IACU,KAAK,CAAC,CAAC,SAAS,cAAc,GAAG,GAAG,EAAE,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAKjH;;;;;;;;;OASG;IACU,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAcpG"}
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Transaction = void 0;
4
+ const iden_caluse_1 = require("./clauses/iden.caluse");
5
+ /**
6
+ * Represents an active SQL transaction.
7
+ * All queries are executed on a single dedicated connection.
8
+ */
9
+ class Transaction {
10
+ constructor(conn) {
11
+ this.conn = conn;
12
+ this.isFinished = false;
13
+ }
14
+ /**
15
+ * Returns true if the transaction is still open (not committed or rolled back).
16
+ */
17
+ get isActive() {
18
+ return !this.isFinished;
19
+ }
20
+ checkActive() {
21
+ if (this.isFinished) {
22
+ throw new Error("Transaction is finished");
23
+ }
24
+ }
25
+ /**
26
+ * Commits the current transaction.
27
+ */
28
+ async commit() {
29
+ this.checkActive();
30
+ await this.conn.query `COMMIT`;
31
+ this.isFinished = true;
32
+ }
33
+ /**
34
+ * Rolls back the current transaction.
35
+ */
36
+ async rollback() {
37
+ this.checkActive();
38
+ await this.conn.query `ROLLBACK`;
39
+ this.isFinished = true;
40
+ }
41
+ /**
42
+ * Executes a query within the current transaction.
43
+ */
44
+ async query(strings, ...values) {
45
+ this.checkActive();
46
+ return await this.conn.query(strings, ...values);
47
+ }
48
+ /**
49
+ * Creates a sub-transaction using PostgreSQL SAVEPOINT.
50
+ * If the callback throws, only the actions within this savepoint are rolled back.
51
+ *
52
+ * @example
53
+ * await tx.savepoint('my_point', async (stx) => {
54
+ * await stx.query`INSERT ...`;
55
+ * if (error) throw new Error(); // Only this insert rolls back
56
+ * });
57
+ */
58
+ async savepoint(name, callback) {
59
+ this.checkActive();
60
+ await this.conn.query `SAVEPOINT ${(0, iden_caluse_1.identClause)(name)}`;
61
+ try {
62
+ await callback(this);
63
+ await this.conn.query `RELEASE SAVEPOINT ${(0, iden_caluse_1.identClause)(name)}`;
64
+ }
65
+ catch (err) {
66
+ await this.conn.query `ROLLBACK TO SAVEPOINT ${(0, iden_caluse_1.identClause)(name)}`;
67
+ throw err;
68
+ }
69
+ }
70
+ }
71
+ exports.Transaction = Transaction;
@@ -0,0 +1,8 @@
1
+ import { PoolConfig as PgPoolConfig } from "pg";
2
+ export type PoolConfig = PgPoolConfig;
3
+ export type PreparedStatement<TResult extends any, Tparams extends any[]> = {
4
+ text: string;
5
+ name: string;
6
+ execute: (...args: Tparams) => Promise<TResult[]>;
7
+ };
8
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,IAAI,YAAY,EAAE,MAAM,IAAI,CAAA;AAE/C,MAAM,MAAM,UAAU,GAAG,YAAY,CAAA;AAErC,MAAM,MAAM,iBAAiB,CAAC,OAAO,SAAS,GAAG,EAAE,OAAO,SAAS,GAAG,EAAE,IAAI;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;CACpD,CAAA"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,7 @@
1
+ export type CompiledSqlQuery = {
2
+ text: string;
3
+ args: any[];
4
+ argCounter: number;
5
+ };
6
+ export declare function compileSqlTemplate(strings: TemplateStringsArray, values: any[], argCounter: number): CompiledSqlQuery;
7
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,UAAU,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,oBAAoB,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,gBAAgB,CAyBrH"}
package/dist/utils.js ADDED
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compileSqlTemplate = compileSqlTemplate;
4
+ const base_clause_1 = require("./clauses/base.clause");
5
+ function compileSqlTemplate(strings, values, argCounter) {
6
+ const templateLength = strings.length;
7
+ let text = '';
8
+ let args = [];
9
+ strings.forEach((template, index) => {
10
+ text += template;
11
+ if (index === templateLength - 1)
12
+ return;
13
+ const value = values[index];
14
+ if (value instanceof base_clause_1.Clause) {
15
+ const result = value.map(argCounter);
16
+ argCounter = result.argCounter;
17
+ text += result.text;
18
+ args.push(...result.args);
19
+ }
20
+ else {
21
+ args.push(value);
22
+ text += `$${argCounter}`;
23
+ argCounter++;
24
+ }
25
+ });
26
+ return { text, args, argCounter };
27
+ }
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@m2k-5f/pgtx",
3
+ "version": "1.0.0",
4
+ "description": "Lightweight, high-performance SQL toolkit for node-postgres",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "README.md"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "prepublishOnly": "npm run build"
14
+ },
15
+ "keywords": ["sql", "postgres", "pg", "query-builder", "typescript", "transactions", "prepared-statements"],
16
+ "author": "M2K-5F",
17
+ "license": "MIT",
18
+ "dependencies": {
19
+ "pg": "^8.11.0"
20
+ },
21
+ "devDependencies": {
22
+ "@types/pg": "^8.11.0",
23
+ "typescript": "^5.0.0"
24
+ }
25
+ }