postgresdk 0.16.4 → 0.16.6
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/dist/cli.js +73 -21
- package/dist/index.js +73 -21
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4682,6 +4682,22 @@ const log = {
|
|
|
4682
4682
|
error: (...args: any[]) => console.error("[sdk]", ...args),
|
|
4683
4683
|
};
|
|
4684
4684
|
|
|
4685
|
+
/**
|
|
4686
|
+
* Prepare query parameters for PostgreSQL.
|
|
4687
|
+
* The pg library should handle JSONB automatically, but there are edge cases
|
|
4688
|
+
* where explicit stringification is needed (e.g., certain pg versions or when
|
|
4689
|
+
* objects have been through serialization/deserialization).
|
|
4690
|
+
*/
|
|
4691
|
+
function prepareParams(params: any[]): any[] {
|
|
4692
|
+
return params.map(p => {
|
|
4693
|
+
if (p === null || p === undefined) return p;
|
|
4694
|
+
// Stringify objects/arrays for JSONB - while pg should handle this automatically,
|
|
4695
|
+
// we've observed cases where it fails without explicit stringification
|
|
4696
|
+
if (typeof p === 'object') return JSON.stringify(p);
|
|
4697
|
+
return p;
|
|
4698
|
+
});
|
|
4699
|
+
}
|
|
4700
|
+
|
|
4685
4701
|
/**
|
|
4686
4702
|
* CREATE operation - Insert a new record
|
|
4687
4703
|
*/
|
|
@@ -4701,17 +4717,28 @@ export async function createRecord(
|
|
|
4701
4717
|
const text = \`INSERT INTO "\${ctx.table}" (\${cols.map(c => '"' + c + '"').join(", ")})
|
|
4702
4718
|
VALUES (\${placeholders})
|
|
4703
4719
|
RETURNING *\`;
|
|
4704
|
-
|
|
4720
|
+
|
|
4705
4721
|
log.debug("SQL:", text, "vals:", vals);
|
|
4706
|
-
const { rows } = await ctx.pg.query(text, vals);
|
|
4707
|
-
|
|
4722
|
+
const { rows } = await ctx.pg.query(text, prepareParams(vals));
|
|
4723
|
+
|
|
4708
4724
|
return { data: rows[0] ?? null, status: rows[0] ? 201 : 500 };
|
|
4709
4725
|
} catch (e: any) {
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4726
|
+
// Enhanced logging for JSON validation errors
|
|
4727
|
+
const errorMsg = e?.message ?? "";
|
|
4728
|
+
const isJsonError = errorMsg.includes("invalid input syntax for type json");
|
|
4729
|
+
|
|
4730
|
+
if (isJsonError) {
|
|
4731
|
+
log.error(\`POST \${ctx.table} - Invalid JSON input detected!\`);
|
|
4732
|
+
log.error("Input data that caused error:", JSON.stringify(data, null, 2));
|
|
4733
|
+
log.error("PostgreSQL error:", errorMsg);
|
|
4734
|
+
} else {
|
|
4735
|
+
log.error(\`POST \${ctx.table} error:\`, e?.stack ?? e);
|
|
4736
|
+
}
|
|
4737
|
+
|
|
4738
|
+
return {
|
|
4739
|
+
error: e?.message ?? "Internal error",
|
|
4713
4740
|
...(DEBUG ? { stack: e?.stack } : {}),
|
|
4714
|
-
status: 500
|
|
4741
|
+
status: 500
|
|
4715
4742
|
};
|
|
4716
4743
|
}
|
|
4717
4744
|
}
|
|
@@ -4731,8 +4758,8 @@ export async function getByPk(
|
|
|
4731
4758
|
|
|
4732
4759
|
const text = \`SELECT * FROM "\${ctx.table}" WHERE \${wherePkSql} LIMIT 1\`;
|
|
4733
4760
|
log.debug(\`GET \${ctx.table} by PK:\`, pkValues, "SQL:", text);
|
|
4734
|
-
|
|
4735
|
-
const { rows } = await ctx.pg.query(text, pkValues);
|
|
4761
|
+
|
|
4762
|
+
const { rows } = await ctx.pg.query(text, prepareParams(pkValues));
|
|
4736
4763
|
|
|
4737
4764
|
if (!rows[0]) {
|
|
4738
4765
|
return { data: null, status: 404 };
|
|
@@ -5116,14 +5143,14 @@ export async function listRecords(
|
|
|
5116
5143
|
// Get total count for pagination
|
|
5117
5144
|
const countText = \`SELECT COUNT(*) FROM "\${ctx.table}" \${countWhereSQL}\`;
|
|
5118
5145
|
log.debug(\`LIST \${ctx.table} COUNT SQL:\`, countText, "params:", countParams);
|
|
5119
|
-
const countResult = await ctx.pg.query(countText, countParams);
|
|
5146
|
+
const countResult = await ctx.pg.query(countText, prepareParams(countParams));
|
|
5120
5147
|
const total = parseInt(countResult.rows[0].count, 10);
|
|
5121
5148
|
|
|
5122
5149
|
// Get paginated data
|
|
5123
5150
|
const text = \`SELECT \${selectClause} FROM "\${ctx.table}" \${whereSQL} \${orderBySQL} LIMIT \${limitParam} OFFSET \${offsetParam}\`;
|
|
5124
5151
|
log.debug(\`LIST \${ctx.table} SQL:\`, text, "params:", allParams);
|
|
5125
5152
|
|
|
5126
|
-
const { rows } = await ctx.pg.query(text, allParams);
|
|
5153
|
+
const { rows } = await ctx.pg.query(text, prepareParams(allParams));
|
|
5127
5154
|
|
|
5128
5155
|
// Calculate hasMore
|
|
5129
5156
|
const hasMore = offset + limit < total;
|
|
@@ -5142,7 +5169,18 @@ export async function listRecords(
|
|
|
5142
5169
|
log.debug(\`LIST \${ctx.table} result: \${rows.length} rows, \${total} total, hasMore=\${hasMore}\`);
|
|
5143
5170
|
return metadata;
|
|
5144
5171
|
} catch (e: any) {
|
|
5145
|
-
|
|
5172
|
+
// Enhanced logging for JSON validation errors
|
|
5173
|
+
const errorMsg = e?.message ?? "";
|
|
5174
|
+
const isJsonError = errorMsg.includes("invalid input syntax for type json");
|
|
5175
|
+
|
|
5176
|
+
if (isJsonError) {
|
|
5177
|
+
log.error(\`LIST \${ctx.table} - Invalid JSON input detected in query!\`);
|
|
5178
|
+
log.error("WHERE clause:", JSON.stringify(params.where, null, 2));
|
|
5179
|
+
log.error("PostgreSQL error:", errorMsg);
|
|
5180
|
+
} else {
|
|
5181
|
+
log.error(\`LIST \${ctx.table} error:\`, e?.stack ?? e);
|
|
5182
|
+
}
|
|
5183
|
+
|
|
5146
5184
|
return {
|
|
5147
5185
|
error: e?.message ?? "Internal error",
|
|
5148
5186
|
...(DEBUG ? { stack: e?.stack } : {}),
|
|
@@ -5177,12 +5215,12 @@ export async function updateRecord(
|
|
|
5177
5215
|
const setSql = Object.keys(filteredData)
|
|
5178
5216
|
.map((k, i) => \`"\${k}" = $\${i + pkValues.length + 1}\`)
|
|
5179
5217
|
.join(", ");
|
|
5180
|
-
|
|
5218
|
+
|
|
5181
5219
|
const text = \`UPDATE "\${ctx.table}" SET \${setSql} WHERE \${wherePkSql} RETURNING *\`;
|
|
5182
5220
|
const params = [...pkValues, ...Object.values(filteredData)];
|
|
5183
|
-
|
|
5221
|
+
|
|
5184
5222
|
log.debug(\`PATCH \${ctx.table} SQL:\`, text, "params:", params);
|
|
5185
|
-
const { rows } = await ctx.pg.query(text, params);
|
|
5223
|
+
const { rows } = await ctx.pg.query(text, prepareParams(params));
|
|
5186
5224
|
|
|
5187
5225
|
if (!rows[0]) {
|
|
5188
5226
|
return { data: null, status: 404 };
|
|
@@ -5190,11 +5228,25 @@ export async function updateRecord(
|
|
|
5190
5228
|
|
|
5191
5229
|
return { data: rows[0], status: 200 };
|
|
5192
5230
|
} catch (e: any) {
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5231
|
+
// Enhanced logging for JSON validation errors
|
|
5232
|
+
const errorMsg = e?.message ?? "";
|
|
5233
|
+
const isJsonError = errorMsg.includes("invalid input syntax for type json");
|
|
5234
|
+
|
|
5235
|
+
if (isJsonError) {
|
|
5236
|
+
log.error(\`PATCH \${ctx.table} - Invalid JSON input detected!\`);
|
|
5237
|
+
log.error("Input data that caused error:", JSON.stringify(updateData, null, 2));
|
|
5238
|
+
log.error("Filtered data (sent to DB):", JSON.stringify(Object.fromEntries(
|
|
5239
|
+
Object.entries(updateData).filter(([k]) => !ctx.pkColumns.includes(k))
|
|
5240
|
+
), null, 2));
|
|
5241
|
+
log.error("PostgreSQL error:", errorMsg);
|
|
5242
|
+
} else {
|
|
5243
|
+
log.error(\`PATCH \${ctx.table} error:\`, e?.stack ?? e);
|
|
5244
|
+
}
|
|
5245
|
+
|
|
5246
|
+
return {
|
|
5247
|
+
error: e?.message ?? "Internal error",
|
|
5196
5248
|
...(DEBUG ? { stack: e?.stack } : {}),
|
|
5197
|
-
status: 500
|
|
5249
|
+
status: 500
|
|
5198
5250
|
};
|
|
5199
5251
|
}
|
|
5200
5252
|
}
|
|
@@ -5215,9 +5267,9 @@ export async function deleteRecord(
|
|
|
5215
5267
|
const text = ctx.softDeleteColumn
|
|
5216
5268
|
? \`UPDATE "\${ctx.table}" SET "\${ctx.softDeleteColumn}" = NOW() WHERE \${wherePkSql} RETURNING *\`
|
|
5217
5269
|
: \`DELETE FROM "\${ctx.table}" WHERE \${wherePkSql} RETURNING *\`;
|
|
5218
|
-
|
|
5270
|
+
|
|
5219
5271
|
log.debug(\`DELETE \${ctx.softDeleteColumn ? '(soft)' : ''} \${ctx.table} SQL:\`, text, "pk:", pkValues);
|
|
5220
|
-
const { rows } = await ctx.pg.query(text, pkValues);
|
|
5272
|
+
const { rows } = await ctx.pg.query(text, prepareParams(pkValues));
|
|
5221
5273
|
|
|
5222
5274
|
if (!rows[0]) {
|
|
5223
5275
|
return { data: null, status: 404 };
|
package/dist/index.js
CHANGED
|
@@ -3853,6 +3853,22 @@ const log = {
|
|
|
3853
3853
|
error: (...args: any[]) => console.error("[sdk]", ...args),
|
|
3854
3854
|
};
|
|
3855
3855
|
|
|
3856
|
+
/**
|
|
3857
|
+
* Prepare query parameters for PostgreSQL.
|
|
3858
|
+
* The pg library should handle JSONB automatically, but there are edge cases
|
|
3859
|
+
* where explicit stringification is needed (e.g., certain pg versions or when
|
|
3860
|
+
* objects have been through serialization/deserialization).
|
|
3861
|
+
*/
|
|
3862
|
+
function prepareParams(params: any[]): any[] {
|
|
3863
|
+
return params.map(p => {
|
|
3864
|
+
if (p === null || p === undefined) return p;
|
|
3865
|
+
// Stringify objects/arrays for JSONB - while pg should handle this automatically,
|
|
3866
|
+
// we've observed cases where it fails without explicit stringification
|
|
3867
|
+
if (typeof p === 'object') return JSON.stringify(p);
|
|
3868
|
+
return p;
|
|
3869
|
+
});
|
|
3870
|
+
}
|
|
3871
|
+
|
|
3856
3872
|
/**
|
|
3857
3873
|
* CREATE operation - Insert a new record
|
|
3858
3874
|
*/
|
|
@@ -3872,17 +3888,28 @@ export async function createRecord(
|
|
|
3872
3888
|
const text = \`INSERT INTO "\${ctx.table}" (\${cols.map(c => '"' + c + '"').join(", ")})
|
|
3873
3889
|
VALUES (\${placeholders})
|
|
3874
3890
|
RETURNING *\`;
|
|
3875
|
-
|
|
3891
|
+
|
|
3876
3892
|
log.debug("SQL:", text, "vals:", vals);
|
|
3877
|
-
const { rows } = await ctx.pg.query(text, vals);
|
|
3878
|
-
|
|
3893
|
+
const { rows } = await ctx.pg.query(text, prepareParams(vals));
|
|
3894
|
+
|
|
3879
3895
|
return { data: rows[0] ?? null, status: rows[0] ? 201 : 500 };
|
|
3880
3896
|
} catch (e: any) {
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3897
|
+
// Enhanced logging for JSON validation errors
|
|
3898
|
+
const errorMsg = e?.message ?? "";
|
|
3899
|
+
const isJsonError = errorMsg.includes("invalid input syntax for type json");
|
|
3900
|
+
|
|
3901
|
+
if (isJsonError) {
|
|
3902
|
+
log.error(\`POST \${ctx.table} - Invalid JSON input detected!\`);
|
|
3903
|
+
log.error("Input data that caused error:", JSON.stringify(data, null, 2));
|
|
3904
|
+
log.error("PostgreSQL error:", errorMsg);
|
|
3905
|
+
} else {
|
|
3906
|
+
log.error(\`POST \${ctx.table} error:\`, e?.stack ?? e);
|
|
3907
|
+
}
|
|
3908
|
+
|
|
3909
|
+
return {
|
|
3910
|
+
error: e?.message ?? "Internal error",
|
|
3884
3911
|
...(DEBUG ? { stack: e?.stack } : {}),
|
|
3885
|
-
status: 500
|
|
3912
|
+
status: 500
|
|
3886
3913
|
};
|
|
3887
3914
|
}
|
|
3888
3915
|
}
|
|
@@ -3902,8 +3929,8 @@ export async function getByPk(
|
|
|
3902
3929
|
|
|
3903
3930
|
const text = \`SELECT * FROM "\${ctx.table}" WHERE \${wherePkSql} LIMIT 1\`;
|
|
3904
3931
|
log.debug(\`GET \${ctx.table} by PK:\`, pkValues, "SQL:", text);
|
|
3905
|
-
|
|
3906
|
-
const { rows } = await ctx.pg.query(text, pkValues);
|
|
3932
|
+
|
|
3933
|
+
const { rows } = await ctx.pg.query(text, prepareParams(pkValues));
|
|
3907
3934
|
|
|
3908
3935
|
if (!rows[0]) {
|
|
3909
3936
|
return { data: null, status: 404 };
|
|
@@ -4287,14 +4314,14 @@ export async function listRecords(
|
|
|
4287
4314
|
// Get total count for pagination
|
|
4288
4315
|
const countText = \`SELECT COUNT(*) FROM "\${ctx.table}" \${countWhereSQL}\`;
|
|
4289
4316
|
log.debug(\`LIST \${ctx.table} COUNT SQL:\`, countText, "params:", countParams);
|
|
4290
|
-
const countResult = await ctx.pg.query(countText, countParams);
|
|
4317
|
+
const countResult = await ctx.pg.query(countText, prepareParams(countParams));
|
|
4291
4318
|
const total = parseInt(countResult.rows[0].count, 10);
|
|
4292
4319
|
|
|
4293
4320
|
// Get paginated data
|
|
4294
4321
|
const text = \`SELECT \${selectClause} FROM "\${ctx.table}" \${whereSQL} \${orderBySQL} LIMIT \${limitParam} OFFSET \${offsetParam}\`;
|
|
4295
4322
|
log.debug(\`LIST \${ctx.table} SQL:\`, text, "params:", allParams);
|
|
4296
4323
|
|
|
4297
|
-
const { rows } = await ctx.pg.query(text, allParams);
|
|
4324
|
+
const { rows } = await ctx.pg.query(text, prepareParams(allParams));
|
|
4298
4325
|
|
|
4299
4326
|
// Calculate hasMore
|
|
4300
4327
|
const hasMore = offset + limit < total;
|
|
@@ -4313,7 +4340,18 @@ export async function listRecords(
|
|
|
4313
4340
|
log.debug(\`LIST \${ctx.table} result: \${rows.length} rows, \${total} total, hasMore=\${hasMore}\`);
|
|
4314
4341
|
return metadata;
|
|
4315
4342
|
} catch (e: any) {
|
|
4316
|
-
|
|
4343
|
+
// Enhanced logging for JSON validation errors
|
|
4344
|
+
const errorMsg = e?.message ?? "";
|
|
4345
|
+
const isJsonError = errorMsg.includes("invalid input syntax for type json");
|
|
4346
|
+
|
|
4347
|
+
if (isJsonError) {
|
|
4348
|
+
log.error(\`LIST \${ctx.table} - Invalid JSON input detected in query!\`);
|
|
4349
|
+
log.error("WHERE clause:", JSON.stringify(params.where, null, 2));
|
|
4350
|
+
log.error("PostgreSQL error:", errorMsg);
|
|
4351
|
+
} else {
|
|
4352
|
+
log.error(\`LIST \${ctx.table} error:\`, e?.stack ?? e);
|
|
4353
|
+
}
|
|
4354
|
+
|
|
4317
4355
|
return {
|
|
4318
4356
|
error: e?.message ?? "Internal error",
|
|
4319
4357
|
...(DEBUG ? { stack: e?.stack } : {}),
|
|
@@ -4348,12 +4386,12 @@ export async function updateRecord(
|
|
|
4348
4386
|
const setSql = Object.keys(filteredData)
|
|
4349
4387
|
.map((k, i) => \`"\${k}" = $\${i + pkValues.length + 1}\`)
|
|
4350
4388
|
.join(", ");
|
|
4351
|
-
|
|
4389
|
+
|
|
4352
4390
|
const text = \`UPDATE "\${ctx.table}" SET \${setSql} WHERE \${wherePkSql} RETURNING *\`;
|
|
4353
4391
|
const params = [...pkValues, ...Object.values(filteredData)];
|
|
4354
|
-
|
|
4392
|
+
|
|
4355
4393
|
log.debug(\`PATCH \${ctx.table} SQL:\`, text, "params:", params);
|
|
4356
|
-
const { rows } = await ctx.pg.query(text, params);
|
|
4394
|
+
const { rows } = await ctx.pg.query(text, prepareParams(params));
|
|
4357
4395
|
|
|
4358
4396
|
if (!rows[0]) {
|
|
4359
4397
|
return { data: null, status: 404 };
|
|
@@ -4361,11 +4399,25 @@ export async function updateRecord(
|
|
|
4361
4399
|
|
|
4362
4400
|
return { data: rows[0], status: 200 };
|
|
4363
4401
|
} catch (e: any) {
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4402
|
+
// Enhanced logging for JSON validation errors
|
|
4403
|
+
const errorMsg = e?.message ?? "";
|
|
4404
|
+
const isJsonError = errorMsg.includes("invalid input syntax for type json");
|
|
4405
|
+
|
|
4406
|
+
if (isJsonError) {
|
|
4407
|
+
log.error(\`PATCH \${ctx.table} - Invalid JSON input detected!\`);
|
|
4408
|
+
log.error("Input data that caused error:", JSON.stringify(updateData, null, 2));
|
|
4409
|
+
log.error("Filtered data (sent to DB):", JSON.stringify(Object.fromEntries(
|
|
4410
|
+
Object.entries(updateData).filter(([k]) => !ctx.pkColumns.includes(k))
|
|
4411
|
+
), null, 2));
|
|
4412
|
+
log.error("PostgreSQL error:", errorMsg);
|
|
4413
|
+
} else {
|
|
4414
|
+
log.error(\`PATCH \${ctx.table} error:\`, e?.stack ?? e);
|
|
4415
|
+
}
|
|
4416
|
+
|
|
4417
|
+
return {
|
|
4418
|
+
error: e?.message ?? "Internal error",
|
|
4367
4419
|
...(DEBUG ? { stack: e?.stack } : {}),
|
|
4368
|
-
status: 500
|
|
4420
|
+
status: 500
|
|
4369
4421
|
};
|
|
4370
4422
|
}
|
|
4371
4423
|
}
|
|
@@ -4386,9 +4438,9 @@ export async function deleteRecord(
|
|
|
4386
4438
|
const text = ctx.softDeleteColumn
|
|
4387
4439
|
? \`UPDATE "\${ctx.table}" SET "\${ctx.softDeleteColumn}" = NOW() WHERE \${wherePkSql} RETURNING *\`
|
|
4388
4440
|
: \`DELETE FROM "\${ctx.table}" WHERE \${wherePkSql} RETURNING *\`;
|
|
4389
|
-
|
|
4441
|
+
|
|
4390
4442
|
log.debug(\`DELETE \${ctx.softDeleteColumn ? '(soft)' : ''} \${ctx.table} SQL:\`, text, "pk:", pkValues);
|
|
4391
|
-
const { rows } = await ctx.pg.query(text, pkValues);
|
|
4443
|
+
const { rows } = await ctx.pg.query(text, prepareParams(pkValues));
|
|
4392
4444
|
|
|
4393
4445
|
if (!rows[0]) {
|
|
4394
4446
|
return { data: null, status: 404 };
|