@m2k-5f/pgtx 1.0.4 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -139,6 +139,44 @@ await pool.query`UPDATE users SET ${sql.update(data)} WHERE id = ${1}`
139
139
  // SQL: UPDATE users SET status = $1, last_login = $2 WHERE id = $3
140
140
  ```
141
141
 
142
+ ## 🔄 Null & Undefined Handling
143
+
144
+ Pgtx treats `null` and `undefined` differently to match SQL semantics and JavaScript expectations:
145
+
146
+ | Value | In INSERT | In UPDATE | In VALUES/Parameters | In Arrays |
147
+ |-------|-----------|-----------|----------------------|-----------|
148
+ | `null` | `NULL` | `NULL` | `NULL` | `NULL` |
149
+ | `undefined` | `DEFAULT` | Field skipped | Error | Error |
150
+
151
+ ### Examples
152
+
153
+ ```typescript
154
+ // INSERT: undefined becomes DEFAULT
155
+ await pool.query`
156
+ INSERT INTO users ${sql.insert({
157
+ name: 'Alice',
158
+ age: undefined, // -> DEFAULT
159
+ email: null // -> NULL
160
+ })}
161
+ `
162
+ // SQL: INSERT INTO users (name, age, email) VALUES ($1, DEFAULT, $2)
163
+
164
+ // UPDATE: undefined fields are skipped
165
+ await pool.query`
166
+ UPDATE users SET ${sql.update({
167
+ name: 'Bob',
168
+ age: undefined, // skipped - age remains unchanged
169
+ deleted_at: null // explicitly set to NULL
170
+ })} WHERE id = 1
171
+ `
172
+ // SQL: UPDATE users SET name = $1, deleted_at = $2 WHERE id = 1
173
+
174
+ // Arrays: undefined becomes NULL
175
+ sql.array([1, undefined, 3]) // ❌ TypeError(`Array item at index 1 is undefined`)
176
+
177
+ // Empty arrays throw (use [null] for NULL result)
178
+ sql.array([]) // ❌ Error: Array clause is empty. Use sql.array([null])...
179
+ ```
142
180
 
143
181
  ## 🛡️ Security
144
182
  * **SQL Injection**: Automatically uses native placeholders ($1, $2) for all values.
@@ -1 +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"}
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;CAyBrD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,SAAS,GAAE,MAAa,GAAG,WAAW,CAE/E"}
@@ -11,9 +11,12 @@ class ArrayClause extends base_clause_1.Clause {
11
11
  }
12
12
  map(argCounter) {
13
13
  if (this.array.length === 0)
14
- return { text: 'NULL', args: [], argCounter };
14
+ throw new Error('Array clause is empty.\n Use sql.array([null]) if you want no results, or check your data.');
15
15
  const args = [];
16
- const text = `${this.array.map(value => {
16
+ const text = `${this.array.map((value, index) => {
17
+ if (value === undefined) {
18
+ throw new TypeError(`Array item at index ${index} is undefined`);
19
+ }
17
20
  if (value instanceof base_clause_1.Clause) {
18
21
  const result = value.map(argCounter);
19
22
  args.push(...result.args);
@@ -1 +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"}
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;CASrD;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,aAAa,EAAE,CAAC,uBAE7D"}
@@ -9,6 +9,9 @@ class IdentifierClause extends base_clause_1.Clause {
9
9
  this.value = value;
10
10
  }
11
11
  map(argCounter) {
12
+ if (this.value === undefined) {
13
+ throw new TypeError(`Query parameter undefined at position ${argCounter}`);
14
+ }
12
15
  const text = `"${this.value}"`;
13
16
  const args = [];
14
17
  return { text, args, argCounter };
@@ -0,0 +1,9 @@
1
+ import { Clause } from "./base.clause";
2
+ import { CompiledSqlQuery } from "../utils";
3
+ export declare class InsertClause<T extends Record<string, any>> extends Clause {
4
+ readonly inserts: T[];
5
+ constructor(inserts: T[]);
6
+ map(argCounter: number): CompiledSqlQuery;
7
+ }
8
+ export declare function insertClause<T extends Record<string, any>>(...objects: [T, ...NoInfer<T>[]]): InsertClause<T>;
9
+ //# sourceMappingURL=insert.clause.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insert.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/insert.clause.ts"],"names":[],"mappings":"AAAA,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,OAAO,EAAE,CAAC,EAAE;gBAAZ,OAAO,EAAE,CAAC,EAAE;IAGhB,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB;CA2BrD;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,mBAE3F"}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InsertClause = void 0;
4
+ exports.insertClause = insertClause;
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
+ if (this.inserts.length === 0) {
13
+ throw new Error('Insert clause has no rows to insert.\n' +
14
+ 'Provide at least one object with data.');
15
+ }
16
+ const columns = Object.keys(this.inserts[0]);
17
+ let text = `(${columns.join(', ')}) VALUES `;
18
+ let args = [];
19
+ const valuesText = this.inserts.map(values => {
20
+ return `(${Object.values(values).map(value => {
21
+ if (value === undefined)
22
+ return "DEFAULT";
23
+ args.push(value);
24
+ return `$${argCounter++}`;
25
+ }).join(", ")})`;
26
+ }).join(", ");
27
+ text += valuesText;
28
+ return { text, args, argCounter };
29
+ }
30
+ }
31
+ exports.InsertClause = InsertClause;
32
+ function insertClause(...objects) {
33
+ return new InsertClause(objects);
34
+ }
@@ -1 +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"}
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;CAMrD;AAGD,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAExE"}
@@ -9,6 +9,9 @@ class StaticClause extends base_clause_1.Clause {
9
9
  this.value = value;
10
10
  }
11
11
  map(argCounter) {
12
+ if (this.value === undefined) {
13
+ throw new TypeError(`Query parameter undefined at position ${argCounter}`);
14
+ }
12
15
  return { text: this.value, args: [], argCounter };
13
16
  }
14
17
  }
@@ -2,9 +2,8 @@ import { Clause } from "./base.clause";
2
2
  import { CompiledSqlQuery } from "../utils";
3
3
  export declare class UpdateClause<T extends Record<string, any>> extends Clause {
4
4
  readonly updateMap: T;
5
- readonly columns?: (keyof T)[] | undefined;
6
- constructor(updateMap: T, columns?: (keyof T)[] | undefined);
5
+ constructor(updateMap: T);
7
6
  map(argCounter: number): CompiledSqlQuery;
8
7
  }
9
- export declare function updateClause<T extends Record<string, any>>(object: T, ...updateColumns: (keyof T)[]): UpdateClause<T>;
8
+ export declare function updateClause<T extends Record<string, any>>(object: T): UpdateClause<T>;
10
9
  //# sourceMappingURL=update.clause.d.ts.map
@@ -1 +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"}
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;gBAAZ,SAAS,EAAE,CAAC;IAGhB,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB;CAcrD;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,mBAEpE"}
@@ -4,23 +4,23 @@ exports.UpdateClause = void 0;
4
4
  exports.updateClause = updateClause;
5
5
  const base_clause_1 = require("./base.clause");
6
6
  class UpdateClause extends base_clause_1.Clause {
7
- constructor(updateMap, columns) {
7
+ constructor(updateMap) {
8
8
  super();
9
9
  this.updateMap = updateMap;
10
- this.columns = columns;
11
10
  }
12
11
  map(argCounter) {
13
- const columns = this.columns || Object.keys(this.updateMap);
14
12
  const args = [];
15
- const text = columns
16
- .map((key) => {
17
- args.push(this.updateMap[key]);
18
- return `${key.toString()} = $${argCounter++}`;
13
+ const entries = Object.entries(this.updateMap).filter(([_, value]) => value !== undefined);
14
+ if (entries.length === 0)
15
+ throw new Error('Update clause has no data to update. All values are `undefined`');
16
+ const text = entries.map(([key, value]) => {
17
+ args.push(value);
18
+ return `${key} = $${argCounter++}`;
19
19
  }).join(', ');
20
20
  return { text, args, argCounter };
21
21
  }
22
22
  }
23
23
  exports.UpdateClause = UpdateClause;
24
- function updateClause(object, ...updateColumns) {
25
- return new UpdateClause(object, updateColumns.length > 0 ? updateColumns : undefined);
24
+ function updateClause(object) {
25
+ return new UpdateClause(object);
26
26
  }
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ import { identClause } from "./clauses/iden.caluse";
4
4
  import { Pool as PgtxPool } from "./pool";
5
5
  import { staticClause } from "./clauses/static.clause";
6
6
  import { updateClause } from "./clauses/update.clause";
7
- import { valueClause } from "./clauses/values.clause";
7
+ import { insertClause } from "./clauses/insert.clause";
8
8
  /**
9
9
  * Core SQL tagging utility for Pgtx.
10
10
  * Provides type-safe helpers for building dynamic queries with recursive support.
@@ -22,7 +22,7 @@ export declare const sql: {
22
22
  * sql.insert([{ id: 1 }, { id: 2 }])
23
23
  * // Result: (id) VALUES ($1), ($2)
24
24
  */
25
- insert: typeof valueClause;
25
+ insert: typeof insertClause;
26
26
  /**
27
27
  * Generates a SET clause for UPDATE queries from a JavaScript object.
28
28
  *
@@ -1 +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"}
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,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD;;;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 CHANGED
@@ -7,7 +7,7 @@ const iden_caluse_1 = require("./clauses/iden.caluse");
7
7
  const pool_1 = require("./pool");
8
8
  const static_clause_1 = require("./clauses/static.clause");
9
9
  const update_clause_1 = require("./clauses/update.clause");
10
- const values_clause_1 = require("./clauses/values.clause");
10
+ const insert_clause_1 = require("./clauses/insert.clause");
11
11
  /**
12
12
  * Core SQL tagging utility for Pgtx.
13
13
  * Provides type-safe helpers for building dynamic queries with recursive support.
@@ -25,7 +25,7 @@ exports.sql = {
25
25
  * sql.insert([{ id: 1 }, { id: 2 }])
26
26
  * // Result: (id) VALUES ($1), ($2)
27
27
  */
28
- insert: values_clause_1.valueClause,
28
+ insert: insert_clause_1.insertClause,
29
29
  /**
30
30
  * Generates a SET clause for UPDATE queries from a JavaScript object.
31
31
  *
@@ -1 +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"}
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,CAgCrH"}
package/dist/utils.js CHANGED
@@ -18,6 +18,10 @@ function compileSqlTemplate(strings, values, argCounter) {
18
18
  args.push(...result.args);
19
19
  }
20
20
  else {
21
+ if (value === undefined) {
22
+ throw new TypeError(`Query parameter at position ${argCounter} is undefined.
23
+ Use null if you want NULL in SQL, or ensure the value is defined.`);
24
+ }
21
25
  args.push(value);
22
26
  text += `$${argCounter}`;
23
27
  argCounter++;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m2k-5f/pgtx",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "description": "Lightweight, high-performance SQL toolkit for node-postgres",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -12,9 +12,17 @@
12
12
  "build": "tsc",
13
13
  "prepublishOnly": "npm run build"
14
14
  },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git://github.com/M2K-5F/pgtx.git"
18
+ },
15
19
  "keywords": ["sql", "postgres", "pg", "query-builder", "typescript", "transactions", "prepared-statements"],
16
20
  "author": "M2K-5F",
17
21
  "license": "MIT",
22
+ "homepage": "https://github.com/M2K-5F/pgtx",
23
+ "bugs": {
24
+ "url": "https://github.com/M2K-5F/pgtx/issues"
25
+ },
18
26
  "dependencies": {
19
27
  "pg": "^8.11.0"
20
28
  },