@sprucelabs/postgres-data-store 5.1.174 → 6.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.
- package/build/PostgresDatabase.js +27 -27
- package/build/QueryBuilder.js +19 -15
- package/build/esm/PostgresDatabase.js +4 -2
- package/build/esm/QueryBuilder.js +7 -3
- package/package.json +15 -16
|
@@ -29,8 +29,11 @@ const schema_1 = require("@sprucelabs/schema");
|
|
|
29
29
|
const pg_1 = require("pg");
|
|
30
30
|
const QueryBuilder_1 = __importStar(require("./QueryBuilder"));
|
|
31
31
|
class PostgresDatabase {
|
|
32
|
+
connectionString;
|
|
33
|
+
client;
|
|
34
|
+
idCount = 1;
|
|
35
|
+
queries;
|
|
32
36
|
constructor(connectionString) {
|
|
33
|
-
this.idCount = 1;
|
|
34
37
|
(0, schema_1.assertOptions)({ connectionString }, ['connectionString']);
|
|
35
38
|
this.connectionString = connectionString;
|
|
36
39
|
this.queries = QueryBuilder_1.default.Builder();
|
|
@@ -44,16 +47,15 @@ class PostgresDatabase {
|
|
|
44
47
|
: `${this.idCount++}`;
|
|
45
48
|
}
|
|
46
49
|
async update(collection, query, updates) {
|
|
47
|
-
var _a;
|
|
48
50
|
const { sql, values } = this.queries.update(collection, query, updates, false);
|
|
49
51
|
const results = await this.client.query({
|
|
50
52
|
text: sql,
|
|
51
53
|
values,
|
|
52
54
|
});
|
|
53
|
-
return
|
|
55
|
+
return results.rowCount ?? 0;
|
|
54
56
|
}
|
|
55
57
|
async count(collection, query) {
|
|
56
|
-
const { sql, values } = this.queries.find(collection, query
|
|
58
|
+
const { sql, values } = this.queries.find(collection, query ?? {}, {
|
|
57
59
|
includeFields: ['count(*) as count'],
|
|
58
60
|
});
|
|
59
61
|
const results = await this.client.query({
|
|
@@ -81,7 +83,7 @@ class PostgresDatabase {
|
|
|
81
83
|
return record;
|
|
82
84
|
}
|
|
83
85
|
async find(collection, query, options) {
|
|
84
|
-
const { sql, values } = this.queries.find(collection, query
|
|
86
|
+
const { sql, values } = this.queries.find(collection, query ?? {}, options);
|
|
85
87
|
const results = await this.client.query({
|
|
86
88
|
text: sql,
|
|
87
89
|
values,
|
|
@@ -95,18 +97,19 @@ class PostgresDatabase {
|
|
|
95
97
|
return this.executeGetIndexes(collectionName, false);
|
|
96
98
|
}
|
|
97
99
|
async findOne(collection, query, options) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
const results = await this.find(collection, query, {
|
|
101
|
+
...options,
|
|
102
|
+
limit: 1,
|
|
103
|
+
});
|
|
104
|
+
return results[0] ?? null;
|
|
101
105
|
}
|
|
102
106
|
async delete(collection, query) {
|
|
103
|
-
var _a;
|
|
104
107
|
const { sql, values } = this.queries.delete(collection, query);
|
|
105
108
|
const results = await this.client.query({
|
|
106
109
|
text: sql,
|
|
107
110
|
values,
|
|
108
111
|
});
|
|
109
|
-
return
|
|
112
|
+
return results.rowCount ?? 0;
|
|
110
113
|
}
|
|
111
114
|
async dropDatabase() {
|
|
112
115
|
await this.truncateTables();
|
|
@@ -148,19 +151,18 @@ class PostgresDatabase {
|
|
|
148
151
|
}
|
|
149
152
|
}
|
|
150
153
|
async deleteOne(collection, query) {
|
|
151
|
-
var _a;
|
|
152
154
|
if (!query.id) {
|
|
153
155
|
const match = await this.findOne(collection, query, {
|
|
154
156
|
includeFields: ['id'],
|
|
155
157
|
});
|
|
156
|
-
query = { id: match
|
|
158
|
+
query = { id: match?.id };
|
|
157
159
|
}
|
|
158
160
|
let { sql, values } = this.queries.delete(collection, query);
|
|
159
161
|
const results = await this.client.query({
|
|
160
162
|
text: sql,
|
|
161
163
|
values,
|
|
162
164
|
});
|
|
163
|
-
return
|
|
165
|
+
return results.rowCount ?? 0;
|
|
164
166
|
}
|
|
165
167
|
async truncateTables() {
|
|
166
168
|
const res = await this.client.query(`
|
|
@@ -193,7 +195,7 @@ class PostgresDatabase {
|
|
|
193
195
|
return results;
|
|
194
196
|
}
|
|
195
197
|
catch (err) {
|
|
196
|
-
const parsed = this.parseIndexViolatedForFieldsAndValues(err
|
|
198
|
+
const parsed = this.parseIndexViolatedForFieldsAndValues(err?.detail);
|
|
197
199
|
if (parsed) {
|
|
198
200
|
const { fields, values } = parsed;
|
|
199
201
|
throw new data_stores_1.DataStoresError({
|
|
@@ -208,7 +210,6 @@ class PostgresDatabase {
|
|
|
208
210
|
}
|
|
209
211
|
}
|
|
210
212
|
async connect() {
|
|
211
|
-
var _a, _b, _c;
|
|
212
213
|
this.client = new pg_1.Client({
|
|
213
214
|
connectionString: this.connectionString,
|
|
214
215
|
});
|
|
@@ -217,14 +218,14 @@ class PostgresDatabase {
|
|
|
217
218
|
}
|
|
218
219
|
catch (err) {
|
|
219
220
|
const message = err.message;
|
|
220
|
-
if ((
|
|
221
|
+
if ((err.code ?? message)?.includes('ECONNREFUSED')) {
|
|
221
222
|
throw new data_stores_1.DataStoresError({
|
|
222
223
|
code: 'UNABLE_TO_CONNECT_TO_DB',
|
|
223
224
|
originalError: err,
|
|
224
225
|
});
|
|
225
226
|
}
|
|
226
|
-
if (message
|
|
227
|
-
const match =
|
|
227
|
+
if (message?.includes('does not exist')) {
|
|
228
|
+
const match = message.match(/"([^"]*)"/) ?? ['', ''];
|
|
228
229
|
throw new data_stores_1.DataStoresError({
|
|
229
230
|
code: 'INVALID_DATABASE_NAME',
|
|
230
231
|
suppliedName: match[1],
|
|
@@ -283,12 +284,11 @@ class PostgresDatabase {
|
|
|
283
284
|
const indexesToRemove = existingIndexes.filter((existing) => !indexes.find((index) => this.areIndexesEqual(existing, index)));
|
|
284
285
|
await Promise.all([
|
|
285
286
|
...indexesToAdd.map(async (index) => {
|
|
286
|
-
var _a;
|
|
287
287
|
try {
|
|
288
288
|
await this.executeCreateIndex(collectionName, index, isUnique);
|
|
289
289
|
}
|
|
290
290
|
catch (err) {
|
|
291
|
-
if (
|
|
291
|
+
if (err.options?.code !== 'INDEX_EXISTS') {
|
|
292
292
|
throw new data_stores_1.DataStoresError({
|
|
293
293
|
code: 'DUPLICATE_KEY',
|
|
294
294
|
originalError: err,
|
|
@@ -309,7 +309,6 @@ class PostgresDatabase {
|
|
|
309
309
|
await this.executeCreateIndex(collection, fields, isUnique);
|
|
310
310
|
}
|
|
311
311
|
async executeCreateIndex(collection, fields, isUnique) {
|
|
312
|
-
var _a, _b;
|
|
313
312
|
const indexName = this.generateIndexName(collection, fields);
|
|
314
313
|
const keys = this.generateKeyExpressions(fields);
|
|
315
314
|
const query = `CREATE ${isUnique ? `UNIQUE` : ''} INDEX ${indexName} ON "${collection}" (${keys})`;
|
|
@@ -319,7 +318,7 @@ class PostgresDatabase {
|
|
|
319
318
|
});
|
|
320
319
|
}
|
|
321
320
|
catch (err) {
|
|
322
|
-
if (
|
|
321
|
+
if (err.message?.includes?.('already exists')) {
|
|
323
322
|
throw new data_stores_1.DataStoresError({
|
|
324
323
|
code: 'INDEX_EXISTS',
|
|
325
324
|
collectionName: collection,
|
|
@@ -348,12 +347,14 @@ class PostgresDatabase {
|
|
|
348
347
|
await this.client.end();
|
|
349
348
|
}
|
|
350
349
|
isConnected() {
|
|
351
|
-
|
|
352
|
-
|
|
350
|
+
return this.client
|
|
351
|
+
? //@ts-ignore
|
|
352
|
+
this.client._connected && !this.client._ending
|
|
353
|
+
: false;
|
|
353
354
|
}
|
|
354
355
|
parseIndexViolatedForFieldsAndValues(input) {
|
|
355
356
|
const regex = /Key \((.*)\)=\((.*)\) already exists\./;
|
|
356
|
-
const matches = input
|
|
357
|
+
const matches = input?.match(regex);
|
|
357
358
|
if (!matches) {
|
|
358
359
|
return null;
|
|
359
360
|
}
|
|
@@ -370,12 +371,11 @@ class PostgresDatabase {
|
|
|
370
371
|
return result;
|
|
371
372
|
}
|
|
372
373
|
async query(query, params) {
|
|
373
|
-
var _a;
|
|
374
374
|
const results = await this.client.query({
|
|
375
375
|
text: query,
|
|
376
376
|
values: params,
|
|
377
377
|
});
|
|
378
|
-
return
|
|
378
|
+
return results?.rows ?? [];
|
|
379
379
|
}
|
|
380
380
|
}
|
|
381
381
|
exports.default = PostgresDatabase;
|
package/build/QueryBuilder.js
CHANGED
|
@@ -7,7 +7,7 @@ class QueryBuilder {
|
|
|
7
7
|
return new this();
|
|
8
8
|
}
|
|
9
9
|
find(tableName, query, options) {
|
|
10
|
-
const { includeFields, limit, skip, sort } = options
|
|
10
|
+
const { includeFields, limit, skip, sort } = options ?? {};
|
|
11
11
|
const fields = this.buildColumnListFromIncludeFields(includeFields);
|
|
12
12
|
let sql = `SELECT ${fields} FROM ${this.buildTableName(tableName)}`;
|
|
13
13
|
const { values, sql: where } = this.optionallyBuildWhere(query);
|
|
@@ -31,7 +31,7 @@ class QueryBuilder {
|
|
|
31
31
|
let sql = '';
|
|
32
32
|
const values = [];
|
|
33
33
|
const queryKeys = Object.keys(query);
|
|
34
|
-
if ((queryKeys
|
|
34
|
+
if ((queryKeys ?? []).length > 0) {
|
|
35
35
|
const { set: columnSpecs, values: whereValues } = this.buildSetClause({
|
|
36
36
|
query,
|
|
37
37
|
startingCount: startingPlaceholderCount,
|
|
@@ -52,33 +52,33 @@ class QueryBuilder {
|
|
|
52
52
|
let value = query[k];
|
|
53
53
|
const isNull = value === null && useIsNull;
|
|
54
54
|
const formattedK = this.conditionalQuote(k);
|
|
55
|
-
if (value
|
|
55
|
+
if (value?.$in) {
|
|
56
56
|
values.push(...value.$in.map((v) => this.normalizeValue(v)));
|
|
57
57
|
set.push(`${formattedK} IN (${value.$in
|
|
58
58
|
.map(() => `$${++placeholderCount}`)
|
|
59
59
|
.join(', ')})`);
|
|
60
60
|
}
|
|
61
|
-
else if (value
|
|
61
|
+
else if (value?.$regex) {
|
|
62
62
|
values.push(this.normalizeValue(value.$regex));
|
|
63
63
|
set.push(`${formattedK} ~* $${++placeholderCount}`);
|
|
64
64
|
}
|
|
65
|
-
else if (value
|
|
65
|
+
else if (value?.$lte) {
|
|
66
66
|
values.push(this.normalizeValue(value.$lte));
|
|
67
67
|
set.push(`${formattedK} <= $${++placeholderCount}`);
|
|
68
68
|
}
|
|
69
|
-
else if (value
|
|
69
|
+
else if (value?.$lt) {
|
|
70
70
|
values.push(this.normalizeValue(value.$lt));
|
|
71
71
|
set.push(`${formattedK} < $${++placeholderCount}`);
|
|
72
72
|
}
|
|
73
|
-
else if (value
|
|
73
|
+
else if (value?.$gte) {
|
|
74
74
|
values.push(this.normalizeValue(value.$gte));
|
|
75
75
|
set.push(`${formattedK} >= $${++placeholderCount}`);
|
|
76
76
|
}
|
|
77
|
-
else if (value
|
|
77
|
+
else if (value?.$gt) {
|
|
78
78
|
values.push(this.normalizeValue(value.$gt));
|
|
79
79
|
set.push(`${formattedK} > $${++placeholderCount}`);
|
|
80
80
|
}
|
|
81
|
-
else if (typeof
|
|
81
|
+
else if (typeof value?.$ne !== 'undefined') {
|
|
82
82
|
const v = value.$ne;
|
|
83
83
|
v !== null && values.push(this.normalizeValue(v));
|
|
84
84
|
set.push(`${formattedK} ${v === null ? 'IS NOT NULL' : `!= $${++placeholderCount}`}`);
|
|
@@ -168,7 +168,8 @@ class QueryBuilder {
|
|
|
168
168
|
fields.forEach((f) => {
|
|
169
169
|
values.push(this.fieldValueToSqlValue(record, f));
|
|
170
170
|
let placeholder = `$${++placeholderCount}`;
|
|
171
|
-
if (this.isValueObject(record[f]) &&
|
|
171
|
+
if (this.isValueObject(record[f]) &&
|
|
172
|
+
!Array.isArray(record[f])) {
|
|
172
173
|
placeholder += `::json`;
|
|
173
174
|
}
|
|
174
175
|
placeholders.push(placeholder);
|
|
@@ -195,10 +196,11 @@ class QueryBuilder {
|
|
|
195
196
|
value = JSON.stringify(value);
|
|
196
197
|
}
|
|
197
198
|
}
|
|
198
|
-
return value
|
|
199
|
+
return value ?? null;
|
|
199
200
|
}
|
|
200
201
|
isValueObject(value) {
|
|
201
|
-
return value !== null &&
|
|
202
|
+
return (value !== null &&
|
|
203
|
+
(Array.isArray(value) || typeof value === 'object'));
|
|
202
204
|
}
|
|
203
205
|
buildColumnListFromAllRecords(records) {
|
|
204
206
|
const fields = records.map((r) => Object.keys(r)).flat();
|
|
@@ -225,7 +227,9 @@ class QueryBuilder {
|
|
|
225
227
|
return '';
|
|
226
228
|
}
|
|
227
229
|
buildColumnListFromIncludeFields(includeFields) {
|
|
228
|
-
return !includeFields
|
|
230
|
+
return !includeFields
|
|
231
|
+
? '*'
|
|
232
|
+
: includeFields.map((f) => quote(f)).join(', ');
|
|
229
233
|
}
|
|
230
234
|
update(tableName, query, updates, shouldReturnUpdatedRecords = true) {
|
|
231
235
|
const { set: set, values } = this.buildSetClause({
|
|
@@ -248,7 +252,7 @@ class QueryBuilder {
|
|
|
248
252
|
}
|
|
249
253
|
delete(tableName, query) {
|
|
250
254
|
let sql = `DELETE FROM ${this.buildTableName(tableName)}`;
|
|
251
|
-
const { values, sql: where } = this.optionallyBuildWhere(query
|
|
255
|
+
const { values, sql: where } = this.optionallyBuildWhere(query ?? {});
|
|
252
256
|
sql += where;
|
|
253
257
|
this.log('delete', sql, values);
|
|
254
258
|
return {
|
|
@@ -258,7 +262,7 @@ class QueryBuilder {
|
|
|
258
262
|
}
|
|
259
263
|
upsert(tableName, query, updates) {
|
|
260
264
|
let { sql, values } = this.createWithoutReturning(tableName, [
|
|
261
|
-
|
|
265
|
+
{ ...query, ...updates },
|
|
262
266
|
]);
|
|
263
267
|
const { sql: whereSql, values: whereValues } = this.optionallyBuildWhere(query, values.length);
|
|
264
268
|
const queryFields = this.buildColumnListFromAllRecords([query]);
|
|
@@ -390,8 +390,10 @@ export default class PostgresDatabase {
|
|
|
390
390
|
});
|
|
391
391
|
}
|
|
392
392
|
isConnected() {
|
|
393
|
-
|
|
394
|
-
|
|
393
|
+
return this.client
|
|
394
|
+
? //@ts-ignore
|
|
395
|
+
this.client._connected && !this.client._ending
|
|
396
|
+
: false;
|
|
395
397
|
}
|
|
396
398
|
parseIndexViolatedForFieldsAndValues(input) {
|
|
397
399
|
const regex = /Key \((.*)\)=\((.*)\) already exists\./;
|
|
@@ -165,7 +165,8 @@ export default class QueryBuilder {
|
|
|
165
165
|
fields.forEach((f) => {
|
|
166
166
|
values.push(this.fieldValueToSqlValue(record, f));
|
|
167
167
|
let placeholder = `$${++placeholderCount}`;
|
|
168
|
-
if (this.isValueObject(record[f]) &&
|
|
168
|
+
if (this.isValueObject(record[f]) &&
|
|
169
|
+
!Array.isArray(record[f])) {
|
|
169
170
|
placeholder += `::json`;
|
|
170
171
|
}
|
|
171
172
|
placeholders.push(placeholder);
|
|
@@ -195,7 +196,8 @@ export default class QueryBuilder {
|
|
|
195
196
|
return value !== null && value !== void 0 ? value : null;
|
|
196
197
|
}
|
|
197
198
|
isValueObject(value) {
|
|
198
|
-
return value !== null &&
|
|
199
|
+
return (value !== null &&
|
|
200
|
+
(Array.isArray(value) || typeof value === 'object'));
|
|
199
201
|
}
|
|
200
202
|
buildColumnListFromAllRecords(records) {
|
|
201
203
|
const fields = records.map((r) => Object.keys(r)).flat();
|
|
@@ -222,7 +224,9 @@ export default class QueryBuilder {
|
|
|
222
224
|
return '';
|
|
223
225
|
}
|
|
224
226
|
buildColumnListFromIncludeFields(includeFields) {
|
|
225
|
-
return !includeFields
|
|
227
|
+
return !includeFields
|
|
228
|
+
? '*'
|
|
229
|
+
: includeFields.map((f) => quote(f)).join(', ');
|
|
226
230
|
}
|
|
227
231
|
update(tableName, query, updates, shouldReturnUpdatedRecords = true) {
|
|
228
232
|
const { set: set, values } = this.buildSetClause({
|
package/package.json
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"sprucebot",
|
|
21
21
|
"sprucelabs"
|
|
22
22
|
],
|
|
23
|
-
"version": "
|
|
23
|
+
"version": "6.0.0",
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build.ci": "yarn build.tsc && yarn build.resolve-paths && yarn lint",
|
|
26
26
|
"build.dev": "yarn build.tsc --sourceMap ; yarn resolve-paths.lint",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"clean.build": "rm -rf build/",
|
|
35
35
|
"clean.dependencies": "rm -rf node_modules/ package-lock.json yarn.lock",
|
|
36
36
|
"clean.dist": "rm -rf build/__tests__ build/esm/__tests__",
|
|
37
|
-
"fix.lint": "eslint --fix --cache
|
|
38
|
-
"lint": "eslint --cache
|
|
37
|
+
"fix.lint": "eslint --fix --cache",
|
|
38
|
+
"lint": "eslint --cache",
|
|
39
39
|
"lint.tsc": "tsc -p . --noEmit",
|
|
40
40
|
"post.watch.build": "yarn build.copy-files && yarn build.resolve-paths",
|
|
41
41
|
"rebuild": "yarn clean.all && yarn && yarn build.dev",
|
|
@@ -49,30 +49,29 @@
|
|
|
49
49
|
"watch.tsc": "tsc -w"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@sprucelabs/data-stores": "^
|
|
53
|
-
"@sprucelabs/schema": "^
|
|
52
|
+
"@sprucelabs/data-stores": "^27.0.0",
|
|
53
|
+
"@sprucelabs/schema": "^30.0.0",
|
|
54
54
|
"pg": "^8.11.5"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@sprucelabs/esm-postbuild": "^
|
|
57
|
+
"@sprucelabs/esm-postbuild": "^6.0.1",
|
|
58
58
|
"@sprucelabs/jest-json-reporter": "^7.0.207",
|
|
59
|
-
"@sprucelabs/resolve-path-aliases": "^
|
|
60
|
-
"@sprucelabs/semantic-release": "^
|
|
61
|
-
"@sprucelabs/
|
|
62
|
-
"@sprucelabs/test": "^
|
|
63
|
-
"@
|
|
64
|
-
"@types/
|
|
65
|
-
"@types/pg": "^8.11.4",
|
|
59
|
+
"@sprucelabs/resolve-path-aliases": "^2.0.3",
|
|
60
|
+
"@sprucelabs/semantic-release": "^5.0.1",
|
|
61
|
+
"@sprucelabs/test": "^9.0.2",
|
|
62
|
+
"@sprucelabs/test-utils": "^5.0.1",
|
|
63
|
+
"@types/node": "^20.12.7",
|
|
64
|
+
"@types/pg": "^8.11.5",
|
|
66
65
|
"chokidar-cli": "^3.0.0",
|
|
67
66
|
"concurrently": "^8.2.2",
|
|
68
|
-
"eslint": "^
|
|
69
|
-
"eslint-config-spruce": "^
|
|
67
|
+
"eslint": "^9.0.0",
|
|
68
|
+
"eslint-config-spruce": "^11.2.5",
|
|
70
69
|
"jest": "^29.7.0",
|
|
71
70
|
"jest-circus": "^29.7.0",
|
|
72
71
|
"prettier": "^3.2.5",
|
|
73
72
|
"ts-node": "^10.9.2",
|
|
74
73
|
"tsc-watch": "^6.2.0",
|
|
75
|
-
"typescript": "^5.4.
|
|
74
|
+
"typescript": "^5.4.5"
|
|
76
75
|
},
|
|
77
76
|
"jest": {
|
|
78
77
|
"testRunner": "jest-circus/runner",
|