@m2k-5f/pgtx 1.1.0 → 1.2.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 +9 -5
- package/dist/clauses/insert.clause.d.ts +1 -1
- package/dist/clauses/insert.clause.d.ts.map +1 -1
- package/dist/clauses/insert.clause.js +5 -0
- package/dist/clauses/literal.clause.d.ts +9 -0
- package/dist/clauses/literal.clause.d.ts.map +1 -0
- package/dist/clauses/literal.clause.js +21 -0
- package/dist/clauses/update.clause.d.ts.map +1 -1
- package/dist/connection.d.ts +1 -1
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/transaction.d.ts +1 -1
- package/dist/transaction.d.ts.map +1 -1
- package/dist/transaction.js +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Experience ORM-like convenience (auto-inserts, updates, recursive fragments) wit
|
|
|
7
7
|
|
|
8
8
|
## 🔥 Why Pgtx?
|
|
9
9
|
|
|
10
|
-
* **Zero-Cost Abstraction**: Only ~2% overhead compared to raw `pg.query`.
|
|
10
|
+
* **Zero-Cost Abstraction**: Only ~2% - 0% overhead compared to raw `pg.query`.
|
|
11
11
|
* **Structural Caching**: Uses `WeakMap` to cache SQL templates. Static parts are parsed only once.
|
|
12
12
|
* **Explicit Prepared Statements**: Create and reuse prepared statements with type safety
|
|
13
13
|
* **True Recursion**: Nest `sql.fragment` anywhere. Argument numbering ($1, $2) is managed automatically across all nesting levels.
|
|
@@ -46,8 +46,8 @@ The following results were measured during sequential execution of 10,000 comple
|
|
|
46
46
|
|
|
47
47
|
| Tool | RPS | Avg. Query Time | Performance Overhead |
|
|
48
48
|
| :--- | :---: | :---: | :---: |
|
|
49
|
-
| **Native `pg.query`** | **~
|
|
50
|
-
| **Pgtx** | **~
|
|
49
|
+
| **Native `pg.query`** | **~176** | **5.696 ms** | **0% (Baseline)** |
|
|
50
|
+
| **Pgtx** | **~177** | **5.664 ms** | **-0.56%(zero-cost)** |
|
|
51
51
|
| Typical Node.js ORM | **~58** | 12.5+ ms | > 150% |
|
|
52
52
|
|
|
53
53
|
|
|
@@ -76,10 +76,14 @@ await pool.begin(async (tx) => {
|
|
|
76
76
|
await tx.query`UPDATE accounts SET balance = balance - 100 WHERE id = 1`;
|
|
77
77
|
|
|
78
78
|
// Nested transaction (Savepoint)
|
|
79
|
-
await tx.savepoint('inventory', async (stx) => {
|
|
79
|
+
let err = await tx.savepoint('inventory', async (stx) => {
|
|
80
80
|
await stx.query`UPDATE stock SET count = count - 1 WHERE item_id = ${42}`;
|
|
81
81
|
if (outOfStock) throw new Error(); // Only 'inventory' rolls back
|
|
82
82
|
});
|
|
83
|
+
if (err) {
|
|
84
|
+
// Handle error
|
|
85
|
+
// tx still active & can be commited
|
|
86
|
+
}
|
|
83
87
|
});
|
|
84
88
|
// Main transaction commits or rolls back based on callback success
|
|
85
89
|
```
|
|
@@ -171,7 +175,7 @@ await pool.query`
|
|
|
171
175
|
`
|
|
172
176
|
// SQL: UPDATE users SET name = $1, deleted_at = $2 WHERE id = 1
|
|
173
177
|
|
|
174
|
-
// Arrays: undefined
|
|
178
|
+
// Arrays: undefined throw
|
|
175
179
|
sql.array([1, undefined, 3]) // ❌ TypeError(`Array item at index 1 is undefined`)
|
|
176
180
|
|
|
177
181
|
// Empty arrays throw (use [null] for NULL result)
|
|
@@ -5,5 +5,5 @@ export declare class InsertClause<T extends Record<string, any>> extends Clause
|
|
|
5
5
|
constructor(inserts: T[]);
|
|
6
6
|
map(argCounter: number): CompiledSqlQuery;
|
|
7
7
|
}
|
|
8
|
-
export declare function insertClause<T extends Record<string, any>>(...objects:
|
|
8
|
+
export declare function insertClause<T extends Record<string, any>>(...objects: NoInfer<T>[]): InsertClause<T>;
|
|
9
9
|
//# sourceMappingURL=insert.clause.d.ts.map
|
|
@@ -1 +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;
|
|
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;CAgCrD;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,mBAEnF"}
|
|
@@ -14,9 +14,14 @@ class InsertClause extends base_clause_1.Clause {
|
|
|
14
14
|
'Provide at least one object with data.');
|
|
15
15
|
}
|
|
16
16
|
const columns = Object.keys(this.inserts[0]);
|
|
17
|
+
const columnsCount = columns.length;
|
|
17
18
|
let text = `(${columns.join(', ')}) VALUES `;
|
|
18
19
|
let args = [];
|
|
19
20
|
const valuesText = this.inserts.map(values => {
|
|
21
|
+
if (Object.keys(values).length !== columnsCount)
|
|
22
|
+
throw new Error(`
|
|
23
|
+
all rows must have the same columns
|
|
24
|
+
`);
|
|
20
25
|
return `(${Object.values(values).map(value => {
|
|
21
26
|
if (value === undefined)
|
|
22
27
|
return "DEFAULT";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Clause } from "./base.clause";
|
|
2
|
+
import { CompiledSqlQuery } from "../utils";
|
|
3
|
+
export declare class LiteralClause<T extends string> extends Clause {
|
|
4
|
+
readonly value: T;
|
|
5
|
+
constructor(value: T);
|
|
6
|
+
map(argCounter: number): CompiledSqlQuery;
|
|
7
|
+
}
|
|
8
|
+
export declare function literalClause<T extends string>(value: T): LiteralClause<T>;
|
|
9
|
+
//# sourceMappingURL=literal.clause.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"literal.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/literal.clause.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAC,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,qBAAa,aAAa,CAAC,CAAC,SAAS,MAAM,CAAE,SAAQ,MAAM;IAEnD,QAAQ,CAAC,KAAK,EAAE,CAAC;gBAAR,KAAK,EAAE,CAAC;IAKZ,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB;CAMrD;AAGD,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAE1E"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LiteralClause = void 0;
|
|
4
|
+
exports.literalClause = literalClause;
|
|
5
|
+
const base_clause_1 = require("./base.clause");
|
|
6
|
+
class LiteralClause extends base_clause_1.Clause {
|
|
7
|
+
constructor(value) {
|
|
8
|
+
super();
|
|
9
|
+
this.value = value;
|
|
10
|
+
}
|
|
11
|
+
map(argCounter) {
|
|
12
|
+
if (this.value === undefined) {
|
|
13
|
+
throw new TypeError(`Query parameter undefined at position ${argCounter}`);
|
|
14
|
+
}
|
|
15
|
+
return { text: this.value, args: [], argCounter };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.LiteralClause = LiteralClause;
|
|
19
|
+
function literalClause(value) {
|
|
20
|
+
return new LiteralClause(value);
|
|
21
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/update.clause.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"update.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/update.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,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"}
|
package/dist/connection.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export declare class Connection {
|
|
|
26
26
|
* Releases the connection back to the pool.
|
|
27
27
|
* The connection cannot be used after this call.
|
|
28
28
|
*/
|
|
29
|
-
release(): void
|
|
29
|
+
release(): Promise<void>;
|
|
30
30
|
private checkActive;
|
|
31
31
|
/**
|
|
32
32
|
* Executes a tagged SQL query using structural caching.
|
package/dist/connection.d.ts.map
CHANGED
|
@@ -1 +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;
|
|
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;IACU,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAMrC,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"}
|
package/dist/connection.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { arrayClause } from "./clauses/array.clause";
|
|
|
2
2
|
import { fragmentClause } from "./clauses/fragment.clause";
|
|
3
3
|
import { identClause } from "./clauses/iden.caluse";
|
|
4
4
|
import { Pool as PgtxPool } from "./pool";
|
|
5
|
-
import {
|
|
5
|
+
import { literalClause } from "./clauses/literal.clause";
|
|
6
6
|
import { updateClause } from "./clauses/update.clause";
|
|
7
7
|
import { insertClause } from "./clauses/insert.clause";
|
|
8
8
|
/**
|
|
@@ -51,7 +51,7 @@ export declare const sql: {
|
|
|
51
51
|
* sql.literal('DESC')
|
|
52
52
|
* // Result: DESC
|
|
53
53
|
*/
|
|
54
|
-
literal: typeof
|
|
54
|
+
literal: typeof literalClause;
|
|
55
55
|
/**
|
|
56
56
|
* Creates a reusable, recursive SQL fragment.
|
|
57
57
|
* Fragments can be nested within each other; argument numbering is handled automatically.
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,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
|
@@ -5,7 +5,7 @@ const array_clause_1 = require("./clauses/array.clause");
|
|
|
5
5
|
const fragment_clause_1 = require("./clauses/fragment.clause");
|
|
6
6
|
const iden_caluse_1 = require("./clauses/iden.caluse");
|
|
7
7
|
const pool_1 = require("./pool");
|
|
8
|
-
const
|
|
8
|
+
const literal_clause_1 = require("./clauses/literal.clause");
|
|
9
9
|
const update_clause_1 = require("./clauses/update.clause");
|
|
10
10
|
const insert_clause_1 = require("./clauses/insert.clause");
|
|
11
11
|
/**
|
|
@@ -54,7 +54,7 @@ exports.sql = {
|
|
|
54
54
|
* sql.literal('DESC')
|
|
55
55
|
* // Result: DESC
|
|
56
56
|
*/
|
|
57
|
-
literal:
|
|
57
|
+
literal: literal_clause_1.literalClause,
|
|
58
58
|
/**
|
|
59
59
|
* Creates a reusable, recursive SQL fragment.
|
|
60
60
|
* Fragments can be nested within each other; argument numbering is handled automatically.
|
package/dist/transaction.d.ts
CHANGED
|
@@ -35,6 +35,6 @@ export declare class Transaction {
|
|
|
35
35
|
* if (error) throw new Error(); // Only this insert rolls back
|
|
36
36
|
* });
|
|
37
37
|
*/
|
|
38
|
-
savepoint(name: string, callback: (tx: Transaction) => Promise<void>): Promise<
|
|
38
|
+
savepoint(name: string, callback: (tx: Transaction) => Promise<void>): Promise<Error | null>;
|
|
39
39
|
}
|
|
40
40
|
//# sourceMappingURL=transaction.d.ts.map
|
|
@@ -1 +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;
|
|
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,KAAK,GAAG,IAAI,CAAC;CAe5G"}
|
package/dist/transaction.js
CHANGED
|
@@ -61,10 +61,11 @@ class Transaction {
|
|
|
61
61
|
try {
|
|
62
62
|
await callback(this);
|
|
63
63
|
await this.conn.query `RELEASE SAVEPOINT ${(0, iden_caluse_1.identClause)(name)}`;
|
|
64
|
+
return null;
|
|
64
65
|
}
|
|
65
66
|
catch (err) {
|
|
66
67
|
await this.conn.query `ROLLBACK TO SAVEPOINT ${(0, iden_caluse_1.identClause)(name)}`;
|
|
67
|
-
|
|
68
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
}
|