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.
Files changed (3) hide show
  1. package/dist/cli.js +73 -21
  2. package/dist/index.js +73 -21
  3. 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
- log.error(\`POST \${ctx.table} error:\`, e?.stack ?? e);
4711
- return {
4712
- error: e?.message ?? "Internal error",
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
- log.error(\`LIST \${ctx.table} error:\`, e?.stack ?? e);
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
- log.error(\`PATCH \${ctx.table} error:\`, e?.stack ?? e);
5194
- return {
5195
- error: e?.message ?? "Internal error",
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
- log.error(\`POST \${ctx.table} error:\`, e?.stack ?? e);
3882
- return {
3883
- error: e?.message ?? "Internal error",
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
- log.error(\`LIST \${ctx.table} error:\`, e?.stack ?? e);
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
- log.error(\`PATCH \${ctx.table} error:\`, e?.stack ?? e);
4365
- return {
4366
- error: e?.message ?? "Internal error",
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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postgresdk",
3
- "version": "0.16.4",
3
+ "version": "0.16.6",
4
4
  "description": "Generate a typed server/client SDK from a Postgres schema (includes, Zod, Hono).",
5
5
  "type": "module",
6
6
  "bin": {