postgresdk 0.18.10 → 0.18.12

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 CHANGED
@@ -5,25 +5,43 @@ var __getProtoOf = Object.getPrototypeOf;
5
5
  var __defProp = Object.defineProperty;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ function __accessProp(key) {
9
+ return this[key];
10
+ }
11
+ var __toESMCache_node;
12
+ var __toESMCache_esm;
8
13
  var __toESM = (mod, isNodeMode, target) => {
14
+ var canCache = mod != null && typeof mod === "object";
15
+ if (canCache) {
16
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
+ var cached = cache.get(mod);
18
+ if (cached)
19
+ return cached;
20
+ }
9
21
  target = mod != null ? __create(__getProtoOf(mod)) : {};
10
22
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
11
23
  for (let key of __getOwnPropNames(mod))
12
24
  if (!__hasOwnProp.call(to, key))
13
25
  __defProp(to, key, {
14
- get: () => mod[key],
26
+ get: __accessProp.bind(mod, key),
15
27
  enumerable: true
16
28
  });
29
+ if (canCache)
30
+ cache.set(mod, to);
17
31
  return to;
18
32
  };
19
33
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
+ var __returnValue = (v) => v;
35
+ function __exportSetter(name, newValue) {
36
+ this[name] = __returnValue.bind(null, newValue);
37
+ }
20
38
  var __export = (target, all) => {
21
39
  for (var name in all)
22
40
  __defProp(target, name, {
23
41
  get: all[name],
24
42
  enumerable: true,
25
43
  configurable: true,
26
- set: (newValue) => all[name] = () => newValue
44
+ set: __exportSetter.bind(all, name)
27
45
  });
28
46
  };
29
47
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -2119,7 +2137,7 @@ var exports_cli_init = {};
2119
2137
  __export(exports_cli_init, {
2120
2138
  initCommand: () => initCommand
2121
2139
  });
2122
- import { existsSync as existsSync3, writeFileSync, readFileSync, copyFileSync } from "fs";
2140
+ import { existsSync as existsSync3, writeFileSync, readFileSync as readFileSync2, copyFileSync } from "fs";
2123
2141
  import { resolve } from "path";
2124
2142
  import prompts from "prompts";
2125
2143
  async function initCommand(args) {
@@ -2137,7 +2155,7 @@ async function initCommand(args) {
2137
2155
  }
2138
2156
  console.log(`⚠️ Found existing postgresdk.config.ts
2139
2157
  `);
2140
- const existingContent = readFileSync(configPath, "utf-8");
2158
+ const existingContent = readFileSync2(configPath, "utf-8");
2141
2159
  const existingFields = extractConfigFields(existingContent);
2142
2160
  console.log("\uD83D\uDCCB Existing configuration detected:");
2143
2161
  existingFields.forEach((field) => {
@@ -2529,7 +2547,7 @@ __export(exports_cli_pull, {
2529
2547
  pullCommand: () => pullCommand
2530
2548
  });
2531
2549
  import { writeFile as writeFile2, mkdir as mkdir2, readFile as readFile2 } from "fs/promises";
2532
- import { join as join2, dirname as dirname2, resolve as resolve2 } from "path";
2550
+ import { join as join2, dirname as dirname3, resolve as resolve2 } from "path";
2533
2551
  import { existsSync as existsSync4 } from "fs";
2534
2552
  import { pathToFileURL as pathToFileURL2 } from "url";
2535
2553
  async function pullCommand(args) {
@@ -2616,7 +2634,7 @@ Options:`);
2616
2634
  const changedFiles = [];
2617
2635
  for (const [path, content] of Object.entries(sdk.files)) {
2618
2636
  const fullPath = join2(config.output, path);
2619
- await mkdir2(dirname2(fullPath), { recursive: true });
2637
+ await mkdir2(dirname3(fullPath), { recursive: true });
2620
2638
  let shouldWrite = true;
2621
2639
  if (existsSync4(fullPath)) {
2622
2640
  const existing = await readFile2(fullPath, "utf-8");
@@ -2662,9 +2680,9 @@ var init_cli_pull = () => {};
2662
2680
 
2663
2681
  // src/index.ts
2664
2682
  var import_config = __toESM(require_config(), 1);
2665
- import { join, relative } from "node:path";
2666
- import { pathToFileURL } from "node:url";
2667
- import { existsSync as existsSync2 } from "node:fs";
2683
+ import { join, relative, dirname as dirname2 } from "node:path";
2684
+ import { pathToFileURL, fileURLToPath } from "node:url";
2685
+ import { existsSync as existsSync2, readFileSync } from "node:fs";
2668
2686
 
2669
2687
  // src/introspect.ts
2670
2688
  import { Client } from "pg";
@@ -2935,9 +2953,10 @@ function emitIncludeResolver(graph, useJsExtensions) {
2935
2953
  Select${Type} & {
2936
2954
  [K in keyof TInclude as TInclude[K] extends false | undefined ? never : K]:`;
2937
2955
  for (let i = 0;i < edgeEntries.length; i++) {
2938
- const [relKey, edge] = edgeEntries[i];
2939
- if (!relKey || !edge)
2956
+ const entry = edgeEntries[i];
2957
+ if (!entry)
2940
2958
  continue;
2959
+ const [relKey, edge] = entry;
2941
2960
  const targetType = pascal(edge.target);
2942
2961
  const isLast = i === edgeEntries.length - 1;
2943
2962
  out += `
@@ -3167,11 +3186,15 @@ function isVectorType(pgType) {
3167
3186
  const t = pgType.toLowerCase();
3168
3187
  return t === "vector" || t === "halfvec" || t === "sparsevec" || t === "bit";
3169
3188
  }
3189
+ function isJsonbType(pgType) {
3190
+ return pgType.toLowerCase() === "json" || pgType.toLowerCase() === "jsonb";
3191
+ }
3170
3192
  function emitHonoRoutes(table, _graph, opts) {
3171
3193
  const fileTableName = table.name;
3172
3194
  const Type = pascal(table.name);
3173
3195
  const hasVectorColumns = table.columns.some((c) => isVectorType(c.pgType));
3174
3196
  const vectorColumns = table.columns.filter((c) => isVectorType(c.pgType)).map((c) => c.name);
3197
+ const jsonbColumns = table.columns.filter((c) => isJsonbType(c.pgType)).map((c) => c.name);
3175
3198
  const rawPk = table.pk;
3176
3199
  const pkCols = Array.isArray(rawPk) ? rawPk : rawPk ? [rawPk] : [];
3177
3200
  const safePkCols = pkCols.length ? pkCols : ["id"];
@@ -3258,7 +3281,8 @@ export function register${Type}Routes(app: Hono, deps: { pg: { query: (text: str
3258
3281
  softDeleteColumn: ${softDel ? `"${softDel}"` : "null"},
3259
3282
  includeMethodsDepth: ${opts.includeMethodsDepth},
3260
3283
  allColumnNames: [${table.columns.map((c) => `"${c.name}"`).join(", ")}]${vectorColumns.length > 0 ? `,
3261
- vectorColumns: ${JSON.stringify(vectorColumns)}` : ""}
3284
+ vectorColumns: ${JSON.stringify(vectorColumns)}` : ""}${jsonbColumns.length > 0 ? `,
3285
+ jsonbColumns: ${JSON.stringify(jsonbColumns)}` : ""}
3262
3286
  };
3263
3287
  ${hasAuth ? `
3264
3288
  // \uD83D\uDD10 Auth: protect all routes for this table
@@ -3476,7 +3500,7 @@ function isVectorType2(pgType) {
3476
3500
  const t = pgType.toLowerCase();
3477
3501
  return t === "vector" || t === "halfvec" || t === "sparsevec" || t === "bit";
3478
3502
  }
3479
- function isJsonbType(pgType) {
3503
+ function isJsonbType2(pgType) {
3480
3504
  const t = pgType.toLowerCase();
3481
3505
  return t === "json" || t === "jsonb";
3482
3506
  }
@@ -3502,7 +3526,7 @@ function emitClient(table, graph, opts, model) {
3502
3526
  const Type = pascal(table.name);
3503
3527
  const ext = opts.useJsExtensions ? ".js" : "";
3504
3528
  const hasVectorColumns = table.columns.some((c) => isVectorType2(c.pgType));
3505
- const hasJsonbColumns = table.columns.some((c) => isJsonbType(c.pgType));
3529
+ const hasJsonbColumns = table.columns.some((c) => isJsonbType2(c.pgType));
3506
3530
  if (process.env.SDK_DEBUG) {
3507
3531
  const vectorCols = table.columns.filter((c) => isVectorType2(c.pgType));
3508
3532
  if (vectorCols.length > 0) {
@@ -4972,14 +4996,14 @@ function tsTypeFor(pgType, opts, enums) {
4972
4996
  return "number[]";
4973
4997
  return "string";
4974
4998
  }
4975
- function isJsonbType2(pgType) {
4999
+ function isJsonbType3(pgType) {
4976
5000
  const t = pgType.toLowerCase();
4977
5001
  return t === "json" || t === "jsonb";
4978
5002
  }
4979
5003
  var pascal2 = (s) => s.split(/[_\s-]+/).map((w) => w?.[0] ? w[0].toUpperCase() + w.slice(1) : "").join("");
4980
5004
  function emitTypes(table, opts, enums) {
4981
5005
  const Type = pascal2(table.name);
4982
- const hasJsonbColumns = table.columns.some((col) => isJsonbType2(col.pgType));
5006
+ const hasJsonbColumns = table.columns.some((col) => isJsonbType3(col.pgType));
4983
5007
  const insertFields = table.columns.map((col) => {
4984
5008
  const base = tsTypeFor(col.pgType, opts, enums);
4985
5009
  const optional = col.hasDefault || col.nullable ? "?" : "";
@@ -5647,7 +5671,7 @@ export * from "./include-spec${ext}";
5647
5671
  }
5648
5672
 
5649
5673
  // src/emit-sdk-bundle.ts
5650
- function emitSdkBundle(clientFiles, clientDir) {
5674
+ function emitSdkBundle(clientFiles, clientDir, version) {
5651
5675
  const files = {};
5652
5676
  for (const file of clientFiles) {
5653
5677
  const normalizedClientDir = clientDir.replace(/\\/g, "/");
@@ -5657,7 +5681,6 @@ function emitSdkBundle(clientFiles, clientDir) {
5657
5681
  files[relativePath] = file.content;
5658
5682
  }
5659
5683
  }
5660
- const version = `1.0.0`;
5661
5684
  return `/**
5662
5685
  * AUTO-GENERATED FILE - DO NOT EDIT
5663
5686
  *
@@ -5694,6 +5717,7 @@ export interface OperationContext {
5694
5717
  softDeleteColumn?: string | null;
5695
5718
  includeMethodsDepth: number;
5696
5719
  vectorColumns?: string[];
5720
+ jsonbColumns?: string[];
5697
5721
  allColumnNames?: string[];
5698
5722
  select?: string[];
5699
5723
  exclude?: string[];
@@ -5748,22 +5772,6 @@ function buildColumnList(
5748
5772
  return finalColumns.map(col => \`"\${col}"\`).join(", ");
5749
5773
  }
5750
5774
 
5751
- /**
5752
- * Prepare query parameters for PostgreSQL.
5753
- * The pg library should handle JSONB automatically, but there are edge cases
5754
- * where explicit stringification is needed (e.g., certain pg versions or when
5755
- * objects have been through serialization/deserialization).
5756
- */
5757
- function prepareParams(params: any[]): any[] {
5758
- return params.map(p => {
5759
- if (p === null || p === undefined) return p;
5760
- // Stringify objects/arrays for JSONB - while pg should handle this automatically,
5761
- // we've observed cases where it fails without explicit stringification
5762
- if (typeof p === 'object') return JSON.stringify(p);
5763
- return p;
5764
- });
5765
- }
5766
-
5767
5775
  /**
5768
5776
  * Parse vector columns in retrieved rows.
5769
5777
  * pgvector returns vectors as strings (e.g., "[1.5,2.5,3.5]") which need to be
@@ -5809,8 +5817,13 @@ export async function createRecord(
5809
5817
  VALUES (\${placeholders})
5810
5818
  RETURNING \${returningClause}\`;
5811
5819
 
5812
- log.debug("SQL:", text, "vals:", vals);
5813
- const { rows } = await ctx.pg.query(text, prepareParams(vals));
5820
+ const preparedVals = vals.map((v, i) =>
5821
+ v !== null && v !== undefined && typeof v === 'object' && ctx.jsonbColumns?.includes(cols[i])
5822
+ ? JSON.stringify(v)
5823
+ : v
5824
+ );
5825
+ log.debug("SQL:", text, "vals:", preparedVals);
5826
+ const { rows } = await ctx.pg.query(text, preparedVals);
5814
5827
  const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
5815
5828
 
5816
5829
  return { data: parsedRows[0] ?? null, status: parsedRows[0] ? 201 : 500 };
@@ -5852,7 +5865,7 @@ export async function getByPk(
5852
5865
  const text = \`SELECT \${columns} FROM "\${ctx.table}" WHERE \${wherePkSql} LIMIT 1\`;
5853
5866
  log.debug(\`GET \${ctx.table} by PK:\`, pkValues, "SQL:", text);
5854
5867
 
5855
- const { rows } = await ctx.pg.query(text, prepareParams(pkValues));
5868
+ const { rows } = await ctx.pg.query(text, pkValues);
5856
5869
  const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
5857
5870
 
5858
5871
  if (!parsedRows[0]) {
@@ -6324,10 +6337,15 @@ export async function updateRecord(
6324
6337
 
6325
6338
  const returningClause = buildColumnList(ctx.select, ctx.exclude, ctx.allColumnNames);
6326
6339
  const text = \`UPDATE "\${ctx.table}" SET \${setSql} WHERE \${wherePkSql} RETURNING \${returningClause}\`;
6327
- const params = [...pkValues, ...Object.values(filteredData)];
6340
+ const updateVals = Object.entries(filteredData).map(([col, v]) =>
6341
+ v !== null && v !== undefined && typeof v === 'object' && ctx.jsonbColumns?.includes(col)
6342
+ ? JSON.stringify(v)
6343
+ : v
6344
+ );
6345
+ const params = [...pkValues, ...updateVals];
6328
6346
 
6329
6347
  log.debug(\`PATCH \${ctx.table} SQL:\`, text, "params:", params);
6330
- const { rows } = await ctx.pg.query(text, prepareParams(params));
6348
+ const { rows } = await ctx.pg.query(text, params);
6331
6349
  const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
6332
6350
 
6333
6351
  if (!parsedRows[0]) {
@@ -6378,7 +6396,7 @@ export async function deleteRecord(
6378
6396
  : \`DELETE FROM "\${ctx.table}" WHERE \${wherePkSql} RETURNING \${returningClause}\`;
6379
6397
 
6380
6398
  log.debug(\`DELETE \${ctx.softDeleteColumn ? '(soft)' : ''} \${ctx.table} SQL:\`, text, "pk:", pkValues);
6381
- const { rows } = await ctx.pg.query(text, prepareParams(pkValues));
6399
+ const { rows } = await ctx.pg.query(text, pkValues);
6382
6400
  const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
6383
6401
 
6384
6402
  if (!parsedRows[0]) {
@@ -7131,6 +7149,9 @@ function generateTestCases(table, sampleData, updateData, hasForeignKeys = false
7131
7149
  // src/index.ts
7132
7150
  init_emit_sdk_contract();
7133
7151
  init_utils();
7152
+ var __filename2 = fileURLToPath(import.meta.url);
7153
+ var __dirname2 = dirname2(__filename2);
7154
+ var { version: CLI_VERSION } = JSON.parse(readFileSync(join(__dirname2, "../package.json"), "utf-8"));
7134
7155
  async function generate(configPath) {
7135
7156
  if (!existsSync2(configPath)) {
7136
7157
  throw new Error(`Config file not found: ${configPath}
@@ -7143,6 +7164,7 @@ async function generate(configPath) {
7143
7164
  const rawCfg = module.default || module;
7144
7165
  const normalizedAuth = normalizeAuthConfig(rawCfg.auth);
7145
7166
  const cfg = { ...rawCfg, auth: normalizedAuth };
7167
+ console.log(`\uD83D\uDCE6 postgresdk v${CLI_VERSION}`);
7146
7168
  console.log("\uD83D\uDD0D Introspecting database...");
7147
7169
  const model = await introspect(cfg.connectionString, cfg.schema || "public");
7148
7170
  console.log("\uD83D\uDD17 Building relationship graph...");
@@ -7284,7 +7306,7 @@ async function generate(configPath) {
7284
7306
  });
7285
7307
  files.push({
7286
7308
  path: join(serverDir, "sdk-bundle.ts"),
7287
- content: emitSdkBundle(clientFiles, clientDir)
7309
+ content: emitSdkBundle(clientFiles, clientDir, CLI_VERSION)
7288
7310
  });
7289
7311
  if (generateTests) {
7290
7312
  console.log("\uD83E\uDDEA Generating tests...");
@@ -7360,12 +7382,12 @@ async function generate(configPath) {
7360
7382
  // src/cli.ts
7361
7383
  var import_config2 = __toESM(require_config(), 1);
7362
7384
  import { resolve as resolve3 } from "node:path";
7363
- import { readFileSync as readFileSync2 } from "node:fs";
7364
- import { fileURLToPath } from "node:url";
7365
- import { dirname as dirname3, join as join3 } from "node:path";
7366
- var __filename2 = fileURLToPath(import.meta.url);
7367
- var __dirname2 = dirname3(__filename2);
7368
- var packageJson = JSON.parse(readFileSync2(join3(__dirname2, "../package.json"), "utf-8"));
7385
+ import { readFileSync as readFileSync3 } from "node:fs";
7386
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
7387
+ import { dirname as dirname4, join as join3 } from "node:path";
7388
+ var __filename3 = fileURLToPath2(import.meta.url);
7389
+ var __dirname3 = dirname4(__filename3);
7390
+ var packageJson = JSON.parse(readFileSync3(join3(__dirname3, "../package.json"), "utf-8"));
7369
7391
  var VERSION = packageJson.version;
7370
7392
  var args = process.argv.slice(2);
7371
7393
  var command = args[0];
@@ -5,4 +5,4 @@
5
5
  export declare function emitSdkBundle(clientFiles: {
6
6
  path: string;
7
7
  content: string;
8
- }[], clientDir: string): string;
8
+ }[], clientDir: string, version: string): string;
package/dist/index.js CHANGED
@@ -4,25 +4,43 @@ var __getProtoOf = Object.getPrototypeOf;
4
4
  var __defProp = Object.defineProperty;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
7
12
  var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
8
20
  target = mod != null ? __create(__getProtoOf(mod)) : {};
9
21
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
22
  for (let key of __getOwnPropNames(mod))
11
23
  if (!__hasOwnProp.call(to, key))
12
24
  __defProp(to, key, {
13
- get: () => mod[key],
25
+ get: __accessProp.bind(mod, key),
14
26
  enumerable: true
15
27
  });
28
+ if (canCache)
29
+ cache.set(mod, to);
16
30
  return to;
17
31
  };
18
32
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
+ var __returnValue = (v) => v;
34
+ function __exportSetter(name, newValue) {
35
+ this[name] = __returnValue.bind(null, newValue);
36
+ }
19
37
  var __export = (target, all) => {
20
38
  for (var name in all)
21
39
  __defProp(target, name, {
22
40
  get: all[name],
23
41
  enumerable: true,
24
42
  configurable: true,
25
- set: (newValue) => all[name] = () => newValue
43
+ set: __exportSetter.bind(all, name)
26
44
  });
27
45
  };
28
46
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -1701,9 +1719,9 @@ var init_emit_sdk_contract = __esm(() => {
1701
1719
 
1702
1720
  // src/index.ts
1703
1721
  var import_config = __toESM(require_config(), 1);
1704
- import { join, relative } from "node:path";
1705
- import { pathToFileURL } from "node:url";
1706
- import { existsSync as existsSync2 } from "node:fs";
1722
+ import { join, relative, dirname as dirname2 } from "node:path";
1723
+ import { pathToFileURL, fileURLToPath } from "node:url";
1724
+ import { existsSync as existsSync2, readFileSync } from "node:fs";
1707
1725
 
1708
1726
  // src/introspect.ts
1709
1727
  import { Client } from "pg";
@@ -1974,9 +1992,10 @@ function emitIncludeResolver(graph, useJsExtensions) {
1974
1992
  Select${Type} & {
1975
1993
  [K in keyof TInclude as TInclude[K] extends false | undefined ? never : K]:`;
1976
1994
  for (let i = 0;i < edgeEntries.length; i++) {
1977
- const [relKey, edge] = edgeEntries[i];
1978
- if (!relKey || !edge)
1995
+ const entry = edgeEntries[i];
1996
+ if (!entry)
1979
1997
  continue;
1998
+ const [relKey, edge] = entry;
1980
1999
  const targetType = pascal(edge.target);
1981
2000
  const isLast = i === edgeEntries.length - 1;
1982
2001
  out += `
@@ -2206,11 +2225,15 @@ function isVectorType(pgType) {
2206
2225
  const t = pgType.toLowerCase();
2207
2226
  return t === "vector" || t === "halfvec" || t === "sparsevec" || t === "bit";
2208
2227
  }
2228
+ function isJsonbType(pgType) {
2229
+ return pgType.toLowerCase() === "json" || pgType.toLowerCase() === "jsonb";
2230
+ }
2209
2231
  function emitHonoRoutes(table, _graph, opts) {
2210
2232
  const fileTableName = table.name;
2211
2233
  const Type = pascal(table.name);
2212
2234
  const hasVectorColumns = table.columns.some((c) => isVectorType(c.pgType));
2213
2235
  const vectorColumns = table.columns.filter((c) => isVectorType(c.pgType)).map((c) => c.name);
2236
+ const jsonbColumns = table.columns.filter((c) => isJsonbType(c.pgType)).map((c) => c.name);
2214
2237
  const rawPk = table.pk;
2215
2238
  const pkCols = Array.isArray(rawPk) ? rawPk : rawPk ? [rawPk] : [];
2216
2239
  const safePkCols = pkCols.length ? pkCols : ["id"];
@@ -2297,7 +2320,8 @@ export function register${Type}Routes(app: Hono, deps: { pg: { query: (text: str
2297
2320
  softDeleteColumn: ${softDel ? `"${softDel}"` : "null"},
2298
2321
  includeMethodsDepth: ${opts.includeMethodsDepth},
2299
2322
  allColumnNames: [${table.columns.map((c) => `"${c.name}"`).join(", ")}]${vectorColumns.length > 0 ? `,
2300
- vectorColumns: ${JSON.stringify(vectorColumns)}` : ""}
2323
+ vectorColumns: ${JSON.stringify(vectorColumns)}` : ""}${jsonbColumns.length > 0 ? `,
2324
+ jsonbColumns: ${JSON.stringify(jsonbColumns)}` : ""}
2301
2325
  };
2302
2326
  ${hasAuth ? `
2303
2327
  // \uD83D\uDD10 Auth: protect all routes for this table
@@ -2515,7 +2539,7 @@ function isVectorType2(pgType) {
2515
2539
  const t = pgType.toLowerCase();
2516
2540
  return t === "vector" || t === "halfvec" || t === "sparsevec" || t === "bit";
2517
2541
  }
2518
- function isJsonbType(pgType) {
2542
+ function isJsonbType2(pgType) {
2519
2543
  const t = pgType.toLowerCase();
2520
2544
  return t === "json" || t === "jsonb";
2521
2545
  }
@@ -2541,7 +2565,7 @@ function emitClient(table, graph, opts, model) {
2541
2565
  const Type = pascal(table.name);
2542
2566
  const ext = opts.useJsExtensions ? ".js" : "";
2543
2567
  const hasVectorColumns = table.columns.some((c) => isVectorType2(c.pgType));
2544
- const hasJsonbColumns = table.columns.some((c) => isJsonbType(c.pgType));
2568
+ const hasJsonbColumns = table.columns.some((c) => isJsonbType2(c.pgType));
2545
2569
  if (process.env.SDK_DEBUG) {
2546
2570
  const vectorCols = table.columns.filter((c) => isVectorType2(c.pgType));
2547
2571
  if (vectorCols.length > 0) {
@@ -4011,14 +4035,14 @@ function tsTypeFor(pgType, opts, enums) {
4011
4035
  return "number[]";
4012
4036
  return "string";
4013
4037
  }
4014
- function isJsonbType2(pgType) {
4038
+ function isJsonbType3(pgType) {
4015
4039
  const t = pgType.toLowerCase();
4016
4040
  return t === "json" || t === "jsonb";
4017
4041
  }
4018
4042
  var pascal2 = (s) => s.split(/[_\s-]+/).map((w) => w?.[0] ? w[0].toUpperCase() + w.slice(1) : "").join("");
4019
4043
  function emitTypes(table, opts, enums) {
4020
4044
  const Type = pascal2(table.name);
4021
- const hasJsonbColumns = table.columns.some((col) => isJsonbType2(col.pgType));
4045
+ const hasJsonbColumns = table.columns.some((col) => isJsonbType3(col.pgType));
4022
4046
  const insertFields = table.columns.map((col) => {
4023
4047
  const base = tsTypeFor(col.pgType, opts, enums);
4024
4048
  const optional = col.hasDefault || col.nullable ? "?" : "";
@@ -4686,7 +4710,7 @@ export * from "./include-spec${ext}";
4686
4710
  }
4687
4711
 
4688
4712
  // src/emit-sdk-bundle.ts
4689
- function emitSdkBundle(clientFiles, clientDir) {
4713
+ function emitSdkBundle(clientFiles, clientDir, version) {
4690
4714
  const files = {};
4691
4715
  for (const file of clientFiles) {
4692
4716
  const normalizedClientDir = clientDir.replace(/\\/g, "/");
@@ -4696,7 +4720,6 @@ function emitSdkBundle(clientFiles, clientDir) {
4696
4720
  files[relativePath] = file.content;
4697
4721
  }
4698
4722
  }
4699
- const version = `1.0.0`;
4700
4723
  return `/**
4701
4724
  * AUTO-GENERATED FILE - DO NOT EDIT
4702
4725
  *
@@ -4733,6 +4756,7 @@ export interface OperationContext {
4733
4756
  softDeleteColumn?: string | null;
4734
4757
  includeMethodsDepth: number;
4735
4758
  vectorColumns?: string[];
4759
+ jsonbColumns?: string[];
4736
4760
  allColumnNames?: string[];
4737
4761
  select?: string[];
4738
4762
  exclude?: string[];
@@ -4787,22 +4811,6 @@ function buildColumnList(
4787
4811
  return finalColumns.map(col => \`"\${col}"\`).join(", ");
4788
4812
  }
4789
4813
 
4790
- /**
4791
- * Prepare query parameters for PostgreSQL.
4792
- * The pg library should handle JSONB automatically, but there are edge cases
4793
- * where explicit stringification is needed (e.g., certain pg versions or when
4794
- * objects have been through serialization/deserialization).
4795
- */
4796
- function prepareParams(params: any[]): any[] {
4797
- return params.map(p => {
4798
- if (p === null || p === undefined) return p;
4799
- // Stringify objects/arrays for JSONB - while pg should handle this automatically,
4800
- // we've observed cases where it fails without explicit stringification
4801
- if (typeof p === 'object') return JSON.stringify(p);
4802
- return p;
4803
- });
4804
- }
4805
-
4806
4814
  /**
4807
4815
  * Parse vector columns in retrieved rows.
4808
4816
  * pgvector returns vectors as strings (e.g., "[1.5,2.5,3.5]") which need to be
@@ -4848,8 +4856,13 @@ export async function createRecord(
4848
4856
  VALUES (\${placeholders})
4849
4857
  RETURNING \${returningClause}\`;
4850
4858
 
4851
- log.debug("SQL:", text, "vals:", vals);
4852
- const { rows } = await ctx.pg.query(text, prepareParams(vals));
4859
+ const preparedVals = vals.map((v, i) =>
4860
+ v !== null && v !== undefined && typeof v === 'object' && ctx.jsonbColumns?.includes(cols[i])
4861
+ ? JSON.stringify(v)
4862
+ : v
4863
+ );
4864
+ log.debug("SQL:", text, "vals:", preparedVals);
4865
+ const { rows } = await ctx.pg.query(text, preparedVals);
4853
4866
  const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
4854
4867
 
4855
4868
  return { data: parsedRows[0] ?? null, status: parsedRows[0] ? 201 : 500 };
@@ -4891,7 +4904,7 @@ export async function getByPk(
4891
4904
  const text = \`SELECT \${columns} FROM "\${ctx.table}" WHERE \${wherePkSql} LIMIT 1\`;
4892
4905
  log.debug(\`GET \${ctx.table} by PK:\`, pkValues, "SQL:", text);
4893
4906
 
4894
- const { rows } = await ctx.pg.query(text, prepareParams(pkValues));
4907
+ const { rows } = await ctx.pg.query(text, pkValues);
4895
4908
  const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
4896
4909
 
4897
4910
  if (!parsedRows[0]) {
@@ -5363,10 +5376,15 @@ export async function updateRecord(
5363
5376
 
5364
5377
  const returningClause = buildColumnList(ctx.select, ctx.exclude, ctx.allColumnNames);
5365
5378
  const text = \`UPDATE "\${ctx.table}" SET \${setSql} WHERE \${wherePkSql} RETURNING \${returningClause}\`;
5366
- const params = [...pkValues, ...Object.values(filteredData)];
5379
+ const updateVals = Object.entries(filteredData).map(([col, v]) =>
5380
+ v !== null && v !== undefined && typeof v === 'object' && ctx.jsonbColumns?.includes(col)
5381
+ ? JSON.stringify(v)
5382
+ : v
5383
+ );
5384
+ const params = [...pkValues, ...updateVals];
5367
5385
 
5368
5386
  log.debug(\`PATCH \${ctx.table} SQL:\`, text, "params:", params);
5369
- const { rows } = await ctx.pg.query(text, prepareParams(params));
5387
+ const { rows } = await ctx.pg.query(text, params);
5370
5388
  const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
5371
5389
 
5372
5390
  if (!parsedRows[0]) {
@@ -5417,7 +5435,7 @@ export async function deleteRecord(
5417
5435
  : \`DELETE FROM "\${ctx.table}" WHERE \${wherePkSql} RETURNING \${returningClause}\`;
5418
5436
 
5419
5437
  log.debug(\`DELETE \${ctx.softDeleteColumn ? '(soft)' : ''} \${ctx.table} SQL:\`, text, "pk:", pkValues);
5420
- const { rows } = await ctx.pg.query(text, prepareParams(pkValues));
5438
+ const { rows } = await ctx.pg.query(text, pkValues);
5421
5439
  const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
5422
5440
 
5423
5441
  if (!parsedRows[0]) {
@@ -6170,6 +6188,9 @@ function generateTestCases(table, sampleData, updateData, hasForeignKeys = false
6170
6188
  // src/index.ts
6171
6189
  init_emit_sdk_contract();
6172
6190
  init_utils();
6191
+ var __filename2 = fileURLToPath(import.meta.url);
6192
+ var __dirname2 = dirname2(__filename2);
6193
+ var { version: CLI_VERSION } = JSON.parse(readFileSync(join(__dirname2, "../package.json"), "utf-8"));
6173
6194
  async function generate(configPath) {
6174
6195
  if (!existsSync2(configPath)) {
6175
6196
  throw new Error(`Config file not found: ${configPath}
@@ -6182,6 +6203,7 @@ async function generate(configPath) {
6182
6203
  const rawCfg = module.default || module;
6183
6204
  const normalizedAuth = normalizeAuthConfig(rawCfg.auth);
6184
6205
  const cfg = { ...rawCfg, auth: normalizedAuth };
6206
+ console.log(`\uD83D\uDCE6 postgresdk v${CLI_VERSION}`);
6185
6207
  console.log("\uD83D\uDD0D Introspecting database...");
6186
6208
  const model = await introspect(cfg.connectionString, cfg.schema || "public");
6187
6209
  console.log("\uD83D\uDD17 Building relationship graph...");
@@ -6323,7 +6345,7 @@ async function generate(configPath) {
6323
6345
  });
6324
6346
  files.push({
6325
6347
  path: join(serverDir, "sdk-bundle.ts"),
6326
- content: emitSdkBundle(clientFiles, clientDir)
6348
+ content: emitSdkBundle(clientFiles, clientDir, CLI_VERSION)
6327
6349
  });
6328
6350
  if (generateTests) {
6329
6351
  console.log("\uD83E\uDDEA Generating tests...");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postgresdk",
3
- "version": "0.18.10",
3
+ "version": "0.18.12",
4
4
  "description": "Generate a typed server/client SDK from a Postgres schema (includes, Zod, Hono).",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,7 +22,7 @@
22
22
  },
23
23
  "scripts": {
24
24
  "build": "bun build src/cli.ts src/index.ts --outdir dist --target node --format esm --external=pg --external=zod --external=hono --external=prompts --external=node:* && tsc -p tsconfig.build.json --emitDeclarationOnly",
25
- "test": "bun test:write-files && bun test:init && bun test:gen && bun test test/test-where-clause.test.ts && bun test test/test-where-or-and.test.ts && bun test test/test-nested-include-options.test.ts && bun test test/test-include-methods-with-options.test.ts && bun test:gen-with-tests && bun test:pull && bun test:enums && bun test:typecheck && bun test:drizzle-e2e && bun test test/test-numeric-mode-integration.test.ts",
25
+ "test": "bun test:write-files && bun test:init && bun test:gen && bun test test/test-where-clause.test.ts && bun test test/test-where-or-and.test.ts && bun test test/test-nested-include-options.test.ts && bun test test/test-include-methods-with-options.test.ts && bun test:gen-with-tests && bun test:pull && bun test:enums && bun test:typecheck && bun test:drizzle-e2e && bun test test/test-numeric-mode-integration.test.ts && bun test test/test-jsonb-array-serialization.test.ts",
26
26
  "test:write-files": "bun test/test-write-files-if-changed.ts",
27
27
  "test:init": "bun test/test-init.ts",
28
28
  "test:gen": "bun test/test-gen.ts",