postgresdk 0.19.0 → 0.19.2

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
@@ -594,7 +594,7 @@ const [order, updatedUser] = await sdk.$transaction([
594
594
  // TypeScript infers: [SelectOrders, SelectUsers | null]
595
595
  ```
596
596
 
597
- - `$create`, `$update`, `$delete`, `$upsert` are **lazy builders** — nothing executes until `$transaction` is called
597
+ - `$create`, `$update`, `$softDelete`, `$hardDelete`, `$upsert` are **lazy builders** — nothing executes until `$transaction` is called
598
598
  - All ops are Zod-validated **before** `BEGIN` is issued (fail-fast, no partial state)
599
599
  - On any failure the transaction rolls back; an error is thrown with a `.failedAt` index
600
600
 
package/dist/cli.js CHANGED
@@ -2823,7 +2823,7 @@ function emitZod(table, opts, enums) {
2823
2823
  return `z.enum([${values}])`;
2824
2824
  }
2825
2825
  if (t === "uuid")
2826
- return `z.string()`;
2826
+ return `z.string().uuid()`;
2827
2827
  if (t === "bool" || t === "boolean")
2828
2828
  return `z.boolean()`;
2829
2829
  if (t === "int2" || t === "int4" || t === "int8") {
@@ -3662,6 +3662,20 @@ function emitClient(table, graph, opts, model) {
3662
3662
  deleteMethodParts.push(buildDeleteMethod("hardDelete", hasSoftDelete));
3663
3663
  const deleteMethodsCode = deleteMethodParts.join(`
3664
3664
 
3665
+ `);
3666
+ const txDeleteParts = [];
3667
+ if (hasSoftDelete)
3668
+ txDeleteParts.push(` /** Build a lazy soft-DELETE descriptor for use with sdk.$transaction([...]) */
3669
+ ` + ` $softDelete(pk: ${pkType}): TxOp<Select${Type} | null> {
3670
+ ` + ` return { _table: "${table.name}", _op: "softDelete", _pk: ${hasCompositePk ? "pk as Record<string, unknown>" : "pk"} };
3671
+ ` + ` }`);
3672
+ if (exposeHard)
3673
+ txDeleteParts.push(` /** Build a lazy hard-DELETE descriptor for use with sdk.$transaction([...]) */
3674
+ ` + ` $hardDelete(pk: ${pkType}): TxOp<Select${Type} | null> {
3675
+ ` + ` return { _table: "${table.name}", _op: "hardDelete", _pk: ${hasCompositePk ? "pk as Record<string, unknown>" : "pk"} };
3676
+ ` + ` }`);
3677
+ const txDeleteMethodsCode = txDeleteParts.join(`
3678
+
3665
3679
  `);
3666
3680
  return `/**
3667
3681
  * AUTO-GENERATED FILE - DO NOT EDIT
@@ -4178,10 +4192,7 @@ ${deleteMethodsCode}
4178
4192
  return { _table: "${table.name}", _op: "update", _pk: ${hasCompositePk ? "pk as Record<string, unknown>" : "pk"}, _data: data as Record<string, unknown> };
4179
4193
  }
4180
4194
 
4181
- /** Build a lazy DELETE descriptor for use with sdk.$transaction([...]) */
4182
- $delete(pk: ${pkType}): TxOp<Select${Type} | null> {
4183
- return { _table: "${table.name}", _op: "delete", _pk: ${hasCompositePk ? "pk as Record<string, unknown>" : "pk"} };
4184
- }
4195
+ ${txDeleteMethodsCode}
4185
4196
 
4186
4197
  /** Build a lazy UPSERT descriptor for use with sdk.$transaction([...]) */
4187
4198
  $upsert(args: { where: Update${Type}; create: Insert${Type}; update: Update${Type} }): TxOp<Select${Type}> {
@@ -4535,13 +4546,13 @@ export abstract class BaseClient {
4535
4546
  }
4536
4547
 
4537
4548
  /**
4538
- * Lazy operation descriptor returned by $create/$update/$delete.
4549
+ * Lazy operation descriptor returned by $create/$update/$softDelete/$hardDelete/$upsert.
4539
4550
  * \`__resultType\` is a phantom field — never assigned at runtime, exists only
4540
4551
  * so TypeScript can infer the correct tuple element type inside \`$transaction\`.
4541
4552
  */
4542
4553
  export type TxOp<T = unknown> = {
4543
4554
  readonly _table: string;
4544
- readonly _op: "create" | "update" | "delete" | "upsert";
4555
+ readonly _op: "create" | "update" | "softDelete" | "hardDelete" | "upsert";
4545
4556
  readonly _data?: Record<string, unknown>;
4546
4557
  readonly _pk?: string | Record<string, unknown>;
4547
4558
  /** @internal */
@@ -5619,11 +5630,16 @@ function emitHonoRouter(tables, hasAuth, useJsExtensions, pullToken, opts) {
5619
5630
  return c.json({ error: \`Missing pk at index \${i}\`, failedAt: i }, 400);
5620
5631
  }
5621
5632
  validatedOps.push({ op: "update", table: item.table, pk: item.pk, data: parsed.data });
5622
- } else if (item.op === "delete") {
5633
+ } else if (item.op === "softDelete") {
5623
5634
  if (item.pk == null) {
5624
5635
  return c.json({ error: \`Missing pk at index \${i}\`, failedAt: i }, 400);
5625
5636
  }
5626
- validatedOps.push({ op: "delete", table: item.table, pk: item.pk });
5637
+ validatedOps.push({ op: "softDelete", table: item.table, pk: item.pk });
5638
+ } else if (item.op === "hardDelete") {
5639
+ if (item.pk == null) {
5640
+ return c.json({ error: \`Missing pk at index \${i}\`, failedAt: i }, 400);
5641
+ }
5642
+ validatedOps.push({ op: "hardDelete", table: item.table, pk: item.pk });
5627
5643
  } else {
5628
5644
  return c.json({ error: \`Unknown op "\${item?.op}" at index \${i}\`, failedAt: i }, 400);
5629
5645
  }
@@ -5935,6 +5951,18 @@ const log = {
5935
5951
  error: (...args: any[]) => console.error("[sdk]", ...args),
5936
5952
  };
5937
5953
 
5954
+ /**
5955
+ * Checks if a Postgres error is a client input error (invalid syntax, out of range, etc.)
5956
+ * PG error class 22 = "data exception" — covers invalid input syntax for uuid, int, json, etc.
5957
+ * PG error code 23502 = "not_null_violation", 23505 = "unique_violation", 23503 = "foreign_key_violation"
5958
+ */
5959
+ function pgErrorStatus(e: any): number {
5960
+ const code = e?.code;
5961
+ if (typeof code === "string" && code.startsWith("22")) return 400;
5962
+ if (code === "23502" || code === "23505" || code === "23503") return 409;
5963
+ return 500;
5964
+ }
5965
+
5938
5966
  /**
5939
5967
  * Builds SQL column list from select/exclude parameters
5940
5968
  * @param select - Columns to include (mutually exclusive with exclude)
@@ -6035,22 +6063,13 @@ export async function createRecord(
6035
6063
 
6036
6064
  return { data: parsedRows[0] ?? null, status: parsedRows[0] ? 201 : 500 };
6037
6065
  } catch (e: any) {
6038
- // Enhanced logging for JSON validation errors
6039
- const errorMsg = e?.message ?? "";
6040
- const isJsonError = errorMsg.includes("invalid input syntax for type json");
6041
-
6042
- if (isJsonError) {
6043
- log.error(\`POST \${ctx.table} - Invalid JSON input detected!\`);
6044
- log.error("Input data that caused error:", JSON.stringify(data, null, 2));
6045
- log.error("PostgreSQL error:", errorMsg);
6046
- } else {
6047
- log.error(\`POST \${ctx.table} error:\`, e?.stack ?? e);
6048
- }
6066
+ const status = pgErrorStatus(e);
6067
+ log.error(\`POST \${ctx.table} error:\`, e?.stack ?? e);
6049
6068
 
6050
6069
  return {
6051
6070
  error: e?.message ?? "Internal error",
6052
6071
  ...(DEBUG ? { stack: e?.stack } : {}),
6053
- status: 500
6072
+ status
6054
6073
  };
6055
6074
  }
6056
6075
  }
@@ -6125,22 +6144,12 @@ export async function upsertRecord(
6125
6144
 
6126
6145
  return { data: parsedRows[0], status: 200 };
6127
6146
  } catch (e: any) {
6128
- const errorMsg = e?.message ?? "";
6129
- const isJsonError = errorMsg.includes("invalid input syntax for type json");
6130
- if (isJsonError) {
6131
- log.error(\`UPSERT \${ctx.table} - Invalid JSON input detected!\`);
6132
- log.error("Input args that caused error:", JSON.stringify(args, null, 2));
6133
- log.error("Filtered update data (sent to DB):", JSON.stringify(Object.fromEntries(
6134
- Object.entries(args.update).filter(([k]) => !ctx.pkColumns.includes(k))
6135
- ), null, 2));
6136
- log.error("PostgreSQL error:", errorMsg);
6137
- } else {
6138
- log.error(\`UPSERT \${ctx.table} error:\`, e?.stack ?? e);
6139
- }
6147
+ const status = pgErrorStatus(e);
6148
+ log.error(\`UPSERT \${ctx.table} error:\`, e?.stack ?? e);
6140
6149
  return {
6141
6150
  error: e?.message ?? "Internal error",
6142
6151
  ...(DEBUG ? { stack: e?.stack } : {}),
6143
- status: 500
6152
+ status
6144
6153
  };
6145
6154
  }
6146
6155
  }
@@ -6173,11 +6182,12 @@ export async function getByPk(
6173
6182
 
6174
6183
  return { data: parsedRows[0], status: 200 };
6175
6184
  } catch (e: any) {
6185
+ const status = pgErrorStatus(e);
6176
6186
  log.error(\`GET \${ctx.table} error:\`, e?.stack ?? e);
6177
- return {
6178
- error: e?.message ?? "Internal error",
6187
+ return {
6188
+ error: e?.message ?? "Internal error",
6179
6189
  ...(DEBUG ? { stack: e?.stack } : {}),
6180
- status: 500
6190
+ status
6181
6191
  };
6182
6192
  }
6183
6193
  }
@@ -6726,22 +6736,13 @@ export async function listRecords(
6726
6736
  log.debug(\`LIST \${ctx.table} result: \${rows.length} rows, \${total} total, hasMore=\${hasMore}\`);
6727
6737
  return metadata;
6728
6738
  } catch (e: any) {
6729
- // Enhanced logging for JSON validation errors
6730
- const errorMsg = e?.message ?? "";
6731
- const isJsonError = errorMsg.includes("invalid input syntax for type json");
6732
-
6733
- if (isJsonError) {
6734
- log.error(\`LIST \${ctx.table} - Invalid JSON input detected in query!\`);
6735
- log.error("WHERE clause:", JSON.stringify(params.where, null, 2));
6736
- log.error("PostgreSQL error:", errorMsg);
6737
- } else {
6738
- log.error(\`LIST \${ctx.table} error:\`, e?.stack ?? e);
6739
- }
6739
+ const status = pgErrorStatus(e);
6740
+ log.error(\`LIST \${ctx.table} error:\`, e?.stack ?? e);
6740
6741
 
6741
6742
  return {
6742
6743
  error: e?.message ?? "Internal error",
6743
6744
  ...(DEBUG ? { stack: e?.stack } : {}),
6744
- status: 500
6745
+ status
6745
6746
  };
6746
6747
  }
6747
6748
  }
@@ -6793,25 +6794,13 @@ export async function updateRecord(
6793
6794
 
6794
6795
  return { data: parsedRows[0], status: 200 };
6795
6796
  } catch (e: any) {
6796
- // Enhanced logging for JSON validation errors
6797
- const errorMsg = e?.message ?? "";
6798
- const isJsonError = errorMsg.includes("invalid input syntax for type json");
6799
-
6800
- if (isJsonError) {
6801
- log.error(\`PATCH \${ctx.table} - Invalid JSON input detected!\`);
6802
- log.error("Input data that caused error:", JSON.stringify(updateData, null, 2));
6803
- log.error("Filtered data (sent to DB):", JSON.stringify(Object.fromEntries(
6804
- Object.entries(updateData).filter(([k]) => !ctx.pkColumns.includes(k))
6805
- ), null, 2));
6806
- log.error("PostgreSQL error:", errorMsg);
6807
- } else {
6808
- log.error(\`PATCH \${ctx.table} error:\`, e?.stack ?? e);
6809
- }
6797
+ const status = pgErrorStatus(e);
6798
+ log.error(\`PATCH \${ctx.table} error:\`, e?.stack ?? e);
6810
6799
 
6811
6800
  return {
6812
6801
  error: e?.message ?? "Internal error",
6813
6802
  ...(DEBUG ? { stack: e?.stack } : {}),
6814
- status: 500
6803
+ status
6815
6804
  };
6816
6805
  }
6817
6806
  }
@@ -6846,11 +6835,12 @@ export async function deleteRecord(
6846
6835
 
6847
6836
  return { data: parsedRows[0], status: 200 };
6848
6837
  } catch (e: any) {
6838
+ const status = pgErrorStatus(e);
6849
6839
  log.error(\`DELETE \${ctx.table} error:\`, e?.stack ?? e);
6850
6840
  return {
6851
6841
  error: e?.message ?? "Internal error",
6852
6842
  ...(DEBUG ? { stack: e?.stack } : {}),
6853
- status: 500
6843
+ status
6854
6844
  };
6855
6845
  }
6856
6846
  }
@@ -6873,7 +6863,8 @@ export interface TransactionTableMetadata {
6873
6863
  export type TransactionOperation =
6874
6864
  | { op: "create"; table: string; data: Record<string, unknown> }
6875
6865
  | { op: "update"; table: string; pk: string | Record<string, unknown>; data: Record<string, unknown> }
6876
- | { op: "delete"; table: string; pk: string | Record<string, unknown> }
6866
+ | { op: "softDelete"; table: string; pk: string | Record<string, unknown> }
6867
+ | { op: "hardDelete"; table: string; pk: string | Record<string, unknown> }
6877
6868
  | { op: "upsert"; table: string; data: { where: Record<string, unknown>; create: Record<string, unknown>; update: Record<string, unknown> } };
6878
6869
 
6879
6870
  /**
@@ -6941,6 +6932,8 @@ export async function executeTransaction(
6941
6932
  : [op.pk];
6942
6933
  result = op.op === "update"
6943
6934
  ? await updateRecord(ctx, pkValues as string[], op.data)
6935
+ : op.op === "hardDelete"
6936
+ ? await deleteRecord(ctx, pkValues as string[], { hard: true })
6944
6937
  : await deleteRecord(ctx, pkValues as string[]);
6945
6938
  }
6946
6939
 
package/dist/index.js CHANGED
@@ -1863,7 +1863,7 @@ function emitZod(table, opts, enums) {
1863
1863
  return `z.enum([${values}])`;
1864
1864
  }
1865
1865
  if (t === "uuid")
1866
- return `z.string()`;
1866
+ return `z.string().uuid()`;
1867
1867
  if (t === "bool" || t === "boolean")
1868
1868
  return `z.boolean()`;
1869
1869
  if (t === "int2" || t === "int4" || t === "int8") {
@@ -2702,6 +2702,20 @@ function emitClient(table, graph, opts, model) {
2702
2702
  deleteMethodParts.push(buildDeleteMethod("hardDelete", hasSoftDelete));
2703
2703
  const deleteMethodsCode = deleteMethodParts.join(`
2704
2704
 
2705
+ `);
2706
+ const txDeleteParts = [];
2707
+ if (hasSoftDelete)
2708
+ txDeleteParts.push(` /** Build a lazy soft-DELETE descriptor for use with sdk.$transaction([...]) */
2709
+ ` + ` $softDelete(pk: ${pkType}): TxOp<Select${Type} | null> {
2710
+ ` + ` return { _table: "${table.name}", _op: "softDelete", _pk: ${hasCompositePk ? "pk as Record<string, unknown>" : "pk"} };
2711
+ ` + ` }`);
2712
+ if (exposeHard)
2713
+ txDeleteParts.push(` /** Build a lazy hard-DELETE descriptor for use with sdk.$transaction([...]) */
2714
+ ` + ` $hardDelete(pk: ${pkType}): TxOp<Select${Type} | null> {
2715
+ ` + ` return { _table: "${table.name}", _op: "hardDelete", _pk: ${hasCompositePk ? "pk as Record<string, unknown>" : "pk"} };
2716
+ ` + ` }`);
2717
+ const txDeleteMethodsCode = txDeleteParts.join(`
2718
+
2705
2719
  `);
2706
2720
  return `/**
2707
2721
  * AUTO-GENERATED FILE - DO NOT EDIT
@@ -3218,10 +3232,7 @@ ${deleteMethodsCode}
3218
3232
  return { _table: "${table.name}", _op: "update", _pk: ${hasCompositePk ? "pk as Record<string, unknown>" : "pk"}, _data: data as Record<string, unknown> };
3219
3233
  }
3220
3234
 
3221
- /** Build a lazy DELETE descriptor for use with sdk.$transaction([...]) */
3222
- $delete(pk: ${pkType}): TxOp<Select${Type} | null> {
3223
- return { _table: "${table.name}", _op: "delete", _pk: ${hasCompositePk ? "pk as Record<string, unknown>" : "pk"} };
3224
- }
3235
+ ${txDeleteMethodsCode}
3225
3236
 
3226
3237
  /** Build a lazy UPSERT descriptor for use with sdk.$transaction([...]) */
3227
3238
  $upsert(args: { where: Update${Type}; create: Insert${Type}; update: Update${Type} }): TxOp<Select${Type}> {
@@ -3575,13 +3586,13 @@ export abstract class BaseClient {
3575
3586
  }
3576
3587
 
3577
3588
  /**
3578
- * Lazy operation descriptor returned by $create/$update/$delete.
3589
+ * Lazy operation descriptor returned by $create/$update/$softDelete/$hardDelete/$upsert.
3579
3590
  * \`__resultType\` is a phantom field — never assigned at runtime, exists only
3580
3591
  * so TypeScript can infer the correct tuple element type inside \`$transaction\`.
3581
3592
  */
3582
3593
  export type TxOp<T = unknown> = {
3583
3594
  readonly _table: string;
3584
- readonly _op: "create" | "update" | "delete" | "upsert";
3595
+ readonly _op: "create" | "update" | "softDelete" | "hardDelete" | "upsert";
3585
3596
  readonly _data?: Record<string, unknown>;
3586
3597
  readonly _pk?: string | Record<string, unknown>;
3587
3598
  /** @internal */
@@ -4659,11 +4670,16 @@ function emitHonoRouter(tables, hasAuth, useJsExtensions, pullToken, opts) {
4659
4670
  return c.json({ error: \`Missing pk at index \${i}\`, failedAt: i }, 400);
4660
4671
  }
4661
4672
  validatedOps.push({ op: "update", table: item.table, pk: item.pk, data: parsed.data });
4662
- } else if (item.op === "delete") {
4673
+ } else if (item.op === "softDelete") {
4663
4674
  if (item.pk == null) {
4664
4675
  return c.json({ error: \`Missing pk at index \${i}\`, failedAt: i }, 400);
4665
4676
  }
4666
- validatedOps.push({ op: "delete", table: item.table, pk: item.pk });
4677
+ validatedOps.push({ op: "softDelete", table: item.table, pk: item.pk });
4678
+ } else if (item.op === "hardDelete") {
4679
+ if (item.pk == null) {
4680
+ return c.json({ error: \`Missing pk at index \${i}\`, failedAt: i }, 400);
4681
+ }
4682
+ validatedOps.push({ op: "hardDelete", table: item.table, pk: item.pk });
4667
4683
  } else {
4668
4684
  return c.json({ error: \`Unknown op "\${item?.op}" at index \${i}\`, failedAt: i }, 400);
4669
4685
  }
@@ -4975,6 +4991,18 @@ const log = {
4975
4991
  error: (...args: any[]) => console.error("[sdk]", ...args),
4976
4992
  };
4977
4993
 
4994
+ /**
4995
+ * Checks if a Postgres error is a client input error (invalid syntax, out of range, etc.)
4996
+ * PG error class 22 = "data exception" — covers invalid input syntax for uuid, int, json, etc.
4997
+ * PG error code 23502 = "not_null_violation", 23505 = "unique_violation", 23503 = "foreign_key_violation"
4998
+ */
4999
+ function pgErrorStatus(e: any): number {
5000
+ const code = e?.code;
5001
+ if (typeof code === "string" && code.startsWith("22")) return 400;
5002
+ if (code === "23502" || code === "23505" || code === "23503") return 409;
5003
+ return 500;
5004
+ }
5005
+
4978
5006
  /**
4979
5007
  * Builds SQL column list from select/exclude parameters
4980
5008
  * @param select - Columns to include (mutually exclusive with exclude)
@@ -5075,22 +5103,13 @@ export async function createRecord(
5075
5103
 
5076
5104
  return { data: parsedRows[0] ?? null, status: parsedRows[0] ? 201 : 500 };
5077
5105
  } catch (e: any) {
5078
- // Enhanced logging for JSON validation errors
5079
- const errorMsg = e?.message ?? "";
5080
- const isJsonError = errorMsg.includes("invalid input syntax for type json");
5081
-
5082
- if (isJsonError) {
5083
- log.error(\`POST \${ctx.table} - Invalid JSON input detected!\`);
5084
- log.error("Input data that caused error:", JSON.stringify(data, null, 2));
5085
- log.error("PostgreSQL error:", errorMsg);
5086
- } else {
5087
- log.error(\`POST \${ctx.table} error:\`, e?.stack ?? e);
5088
- }
5106
+ const status = pgErrorStatus(e);
5107
+ log.error(\`POST \${ctx.table} error:\`, e?.stack ?? e);
5089
5108
 
5090
5109
  return {
5091
5110
  error: e?.message ?? "Internal error",
5092
5111
  ...(DEBUG ? { stack: e?.stack } : {}),
5093
- status: 500
5112
+ status
5094
5113
  };
5095
5114
  }
5096
5115
  }
@@ -5165,22 +5184,12 @@ export async function upsertRecord(
5165
5184
 
5166
5185
  return { data: parsedRows[0], status: 200 };
5167
5186
  } catch (e: any) {
5168
- const errorMsg = e?.message ?? "";
5169
- const isJsonError = errorMsg.includes("invalid input syntax for type json");
5170
- if (isJsonError) {
5171
- log.error(\`UPSERT \${ctx.table} - Invalid JSON input detected!\`);
5172
- log.error("Input args that caused error:", JSON.stringify(args, null, 2));
5173
- log.error("Filtered update data (sent to DB):", JSON.stringify(Object.fromEntries(
5174
- Object.entries(args.update).filter(([k]) => !ctx.pkColumns.includes(k))
5175
- ), null, 2));
5176
- log.error("PostgreSQL error:", errorMsg);
5177
- } else {
5178
- log.error(\`UPSERT \${ctx.table} error:\`, e?.stack ?? e);
5179
- }
5187
+ const status = pgErrorStatus(e);
5188
+ log.error(\`UPSERT \${ctx.table} error:\`, e?.stack ?? e);
5180
5189
  return {
5181
5190
  error: e?.message ?? "Internal error",
5182
5191
  ...(DEBUG ? { stack: e?.stack } : {}),
5183
- status: 500
5192
+ status
5184
5193
  };
5185
5194
  }
5186
5195
  }
@@ -5213,11 +5222,12 @@ export async function getByPk(
5213
5222
 
5214
5223
  return { data: parsedRows[0], status: 200 };
5215
5224
  } catch (e: any) {
5225
+ const status = pgErrorStatus(e);
5216
5226
  log.error(\`GET \${ctx.table} error:\`, e?.stack ?? e);
5217
- return {
5218
- error: e?.message ?? "Internal error",
5227
+ return {
5228
+ error: e?.message ?? "Internal error",
5219
5229
  ...(DEBUG ? { stack: e?.stack } : {}),
5220
- status: 500
5230
+ status
5221
5231
  };
5222
5232
  }
5223
5233
  }
@@ -5766,22 +5776,13 @@ export async function listRecords(
5766
5776
  log.debug(\`LIST \${ctx.table} result: \${rows.length} rows, \${total} total, hasMore=\${hasMore}\`);
5767
5777
  return metadata;
5768
5778
  } catch (e: any) {
5769
- // Enhanced logging for JSON validation errors
5770
- const errorMsg = e?.message ?? "";
5771
- const isJsonError = errorMsg.includes("invalid input syntax for type json");
5772
-
5773
- if (isJsonError) {
5774
- log.error(\`LIST \${ctx.table} - Invalid JSON input detected in query!\`);
5775
- log.error("WHERE clause:", JSON.stringify(params.where, null, 2));
5776
- log.error("PostgreSQL error:", errorMsg);
5777
- } else {
5778
- log.error(\`LIST \${ctx.table} error:\`, e?.stack ?? e);
5779
- }
5779
+ const status = pgErrorStatus(e);
5780
+ log.error(\`LIST \${ctx.table} error:\`, e?.stack ?? e);
5780
5781
 
5781
5782
  return {
5782
5783
  error: e?.message ?? "Internal error",
5783
5784
  ...(DEBUG ? { stack: e?.stack } : {}),
5784
- status: 500
5785
+ status
5785
5786
  };
5786
5787
  }
5787
5788
  }
@@ -5833,25 +5834,13 @@ export async function updateRecord(
5833
5834
 
5834
5835
  return { data: parsedRows[0], status: 200 };
5835
5836
  } catch (e: any) {
5836
- // Enhanced logging for JSON validation errors
5837
- const errorMsg = e?.message ?? "";
5838
- const isJsonError = errorMsg.includes("invalid input syntax for type json");
5839
-
5840
- if (isJsonError) {
5841
- log.error(\`PATCH \${ctx.table} - Invalid JSON input detected!\`);
5842
- log.error("Input data that caused error:", JSON.stringify(updateData, null, 2));
5843
- log.error("Filtered data (sent to DB):", JSON.stringify(Object.fromEntries(
5844
- Object.entries(updateData).filter(([k]) => !ctx.pkColumns.includes(k))
5845
- ), null, 2));
5846
- log.error("PostgreSQL error:", errorMsg);
5847
- } else {
5848
- log.error(\`PATCH \${ctx.table} error:\`, e?.stack ?? e);
5849
- }
5837
+ const status = pgErrorStatus(e);
5838
+ log.error(\`PATCH \${ctx.table} error:\`, e?.stack ?? e);
5850
5839
 
5851
5840
  return {
5852
5841
  error: e?.message ?? "Internal error",
5853
5842
  ...(DEBUG ? { stack: e?.stack } : {}),
5854
- status: 500
5843
+ status
5855
5844
  };
5856
5845
  }
5857
5846
  }
@@ -5886,11 +5875,12 @@ export async function deleteRecord(
5886
5875
 
5887
5876
  return { data: parsedRows[0], status: 200 };
5888
5877
  } catch (e: any) {
5878
+ const status = pgErrorStatus(e);
5889
5879
  log.error(\`DELETE \${ctx.table} error:\`, e?.stack ?? e);
5890
5880
  return {
5891
5881
  error: e?.message ?? "Internal error",
5892
5882
  ...(DEBUG ? { stack: e?.stack } : {}),
5893
- status: 500
5883
+ status
5894
5884
  };
5895
5885
  }
5896
5886
  }
@@ -5913,7 +5903,8 @@ export interface TransactionTableMetadata {
5913
5903
  export type TransactionOperation =
5914
5904
  | { op: "create"; table: string; data: Record<string, unknown> }
5915
5905
  | { op: "update"; table: string; pk: string | Record<string, unknown>; data: Record<string, unknown> }
5916
- | { op: "delete"; table: string; pk: string | Record<string, unknown> }
5906
+ | { op: "softDelete"; table: string; pk: string | Record<string, unknown> }
5907
+ | { op: "hardDelete"; table: string; pk: string | Record<string, unknown> }
5917
5908
  | { op: "upsert"; table: string; data: { where: Record<string, unknown>; create: Record<string, unknown>; update: Record<string, unknown> } };
5918
5909
 
5919
5910
  /**
@@ -5981,6 +5972,8 @@ export async function executeTransaction(
5981
5972
  : [op.pk];
5982
5973
  result = op.op === "update"
5983
5974
  ? await updateRecord(ctx, pkValues as string[], op.data)
5975
+ : op.op === "hardDelete"
5976
+ ? await deleteRecord(ctx, pkValues as string[], { hard: true })
5984
5977
  : await deleteRecord(ctx, pkValues as string[]);
5985
5978
  }
5986
5979
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postgresdk",
3
- "version": "0.19.0",
3
+ "version": "0.19.2",
4
4
  "description": "Generate a typed server/client SDK from a Postgres schema (includes, Zod, Hono).",
5
5
  "type": "module",
6
6
  "bin": {