postgresdk 0.18.9 → 0.18.11

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
@@ -567,7 +567,7 @@ const deleted = await sdk.users.delete(123);
567
567
 
568
568
  #### Relationships & Eager Loading
569
569
 
570
- Automatically handles relationships with the `include` parameter:
570
+ Automatically handles relationships with the `include` parameter. **Type inference works automatically** - no manual casts needed:
571
571
 
572
572
  ```typescript
573
573
  // 1:N relationship - Get authors with their books
@@ -575,12 +575,14 @@ const authorsResult = await sdk.authors.list({
575
575
  include: { books: true }
576
576
  });
577
577
  const authors = authorsResult.data;
578
+ // ✅ authors[0].books is automatically typed as SelectBooks[]
578
579
 
579
580
  // M:N relationship - Get books with their tags
580
581
  const booksResult = await sdk.books.list({
581
582
  include: { tags: true }
582
583
  });
583
584
  const books = booksResult.data;
585
+ // ✅ books[0].tags is automatically typed as SelectTags[]
584
586
 
585
587
  // Nested includes - Get authors with books and their tags
586
588
  const nestedResult = await sdk.authors.list({
@@ -591,6 +593,7 @@ const nestedResult = await sdk.authors.list({
591
593
  }
592
594
  });
593
595
  const authorsWithBooksAndTags = nestedResult.data;
596
+ // ✅ TypeScript knows: data[0].books[0].tags exists and is SelectTags[]
594
597
  ```
595
598
 
596
599
  **Typed Include Methods:**
package/dist/cli.js CHANGED
@@ -2119,7 +2119,7 @@ var exports_cli_init = {};
2119
2119
  __export(exports_cli_init, {
2120
2120
  initCommand: () => initCommand
2121
2121
  });
2122
- import { existsSync as existsSync3, writeFileSync, readFileSync, copyFileSync } from "fs";
2122
+ import { existsSync as existsSync3, writeFileSync, readFileSync as readFileSync2, copyFileSync } from "fs";
2123
2123
  import { resolve } from "path";
2124
2124
  import prompts from "prompts";
2125
2125
  async function initCommand(args) {
@@ -2137,7 +2137,7 @@ async function initCommand(args) {
2137
2137
  }
2138
2138
  console.log(`⚠️ Found existing postgresdk.config.ts
2139
2139
  `);
2140
- const existingContent = readFileSync(configPath, "utf-8");
2140
+ const existingContent = readFileSync2(configPath, "utf-8");
2141
2141
  const existingFields = extractConfigFields(existingContent);
2142
2142
  console.log("\uD83D\uDCCB Existing configuration detected:");
2143
2143
  existingFields.forEach((field) => {
@@ -2529,7 +2529,7 @@ __export(exports_cli_pull, {
2529
2529
  pullCommand: () => pullCommand
2530
2530
  });
2531
2531
  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";
2532
+ import { join as join2, dirname as dirname3, resolve as resolve2 } from "path";
2533
2533
  import { existsSync as existsSync4 } from "fs";
2534
2534
  import { pathToFileURL as pathToFileURL2 } from "url";
2535
2535
  async function pullCommand(args) {
@@ -2616,7 +2616,7 @@ Options:`);
2616
2616
  const changedFiles = [];
2617
2617
  for (const [path, content] of Object.entries(sdk.files)) {
2618
2618
  const fullPath = join2(config.output, path);
2619
- await mkdir2(dirname2(fullPath), { recursive: true });
2619
+ await mkdir2(dirname3(fullPath), { recursive: true });
2620
2620
  let shouldWrite = true;
2621
2621
  if (existsSync4(fullPath)) {
2622
2622
  const existing = await readFile2(fullPath, "utf-8");
@@ -2662,9 +2662,9 @@ var init_cli_pull = () => {};
2662
2662
 
2663
2663
  // src/index.ts
2664
2664
  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";
2665
+ import { join, relative, dirname as dirname2 } from "node:path";
2666
+ import { pathToFileURL, fileURLToPath } from "node:url";
2667
+ import { existsSync as existsSync2, readFileSync } from "node:fs";
2668
2668
 
2669
2669
  // src/introspect.ts
2670
2670
  import { Client } from "pg";
@@ -2899,6 +2899,74 @@ function toPascal(s) {
2899
2899
  return s.split(/[_\s-]+/).map((w) => w?.[0] ? w[0].toUpperCase() + w.slice(1) : "").join("");
2900
2900
  }
2901
2901
 
2902
+ // src/emit-include-resolver.ts
2903
+ init_utils();
2904
+ function emitIncludeResolver(graph, useJsExtensions) {
2905
+ const ext = useJsExtensions ? ".js" : "";
2906
+ let out = `/**
2907
+ * AUTO-GENERATED FILE - DO NOT EDIT
2908
+ *
2909
+ * Type helpers for automatic include inference.
2910
+ * These types transform IncludeSpec into the actual return type shape.
2911
+ */
2912
+ `;
2913
+ const tables = Object.keys(graph);
2914
+ for (const table of tables) {
2915
+ out += `import type { Select${pascal(table)} } from "./types/${table}${ext}";
2916
+ `;
2917
+ }
2918
+ for (const table of tables) {
2919
+ out += `import type { ${pascal(table)}IncludeSpec } from "./include-spec${ext}";
2920
+ `;
2921
+ }
2922
+ out += `
2923
+ `;
2924
+ for (const table of tables) {
2925
+ const Type = pascal(table);
2926
+ const edges = graph[table] || {};
2927
+ const edgeEntries = Object.entries(edges);
2928
+ if (edgeEntries.length === 0) {
2929
+ out += `export type ${Type}WithIncludes<TInclude extends ${Type}IncludeSpec> = Select${Type};
2930
+
2931
+ `;
2932
+ continue;
2933
+ }
2934
+ out += `export type ${Type}WithIncludes<TInclude extends ${Type}IncludeSpec> =
2935
+ Select${Type} & {
2936
+ [K in keyof TInclude as TInclude[K] extends false | undefined ? never : K]:`;
2937
+ for (let i = 0;i < edgeEntries.length; i++) {
2938
+ const entry = edgeEntries[i];
2939
+ if (!entry)
2940
+ continue;
2941
+ const [relKey, edge] = entry;
2942
+ const targetType = pascal(edge.target);
2943
+ const isLast = i === edgeEntries.length - 1;
2944
+ out += `
2945
+ K extends '${relKey}' ? `;
2946
+ if (edge.kind === "many") {
2947
+ out += `(
2948
+ TInclude[K] extends { include: infer U extends ${targetType}IncludeSpec }
2949
+ ? Array<${targetType}WithIncludes<U>>
2950
+ : Select${targetType}[]
2951
+ )`;
2952
+ } else {
2953
+ out += `(
2954
+ TInclude[K] extends { include: infer U extends ${targetType}IncludeSpec }
2955
+ ? ${targetType}WithIncludes<U>
2956
+ : Select${targetType}
2957
+ )`;
2958
+ }
2959
+ out += ` :${isLast ? `
2960
+ never` : ""}`;
2961
+ }
2962
+ out += `
2963
+ };
2964
+
2965
+ `;
2966
+ }
2967
+ return out;
2968
+ }
2969
+
2902
2970
  // src/emit-include-builder.ts
2903
2971
  function emitIncludeBuilder(graph, maxDepth) {
2904
2972
  return `/**
@@ -3462,6 +3530,7 @@ function emitClient(table, graph, opts, model) {
3462
3530
  const typeImports = `import type { Insert${Type}, Update${Type}, Select${Type} } from "./types/${table.name}${ext}";`;
3463
3531
  const includeSpecTypes = [table.name, ...Array.from(importedTypes).filter((t) => t !== table.name)];
3464
3532
  const includeSpecImport = `import type { ${includeSpecTypes.map((t) => `${pascal(t)}IncludeSpec`).join(", ")} } from "./include-spec${ext}";`;
3533
+ const includeResolverImport = `import type { ${Type}WithIncludes } from "./include-resolver${ext}";`;
3465
3534
  const otherTableImports = [];
3466
3535
  for (const target of Array.from(importedTypes)) {
3467
3536
  if (target !== table.name) {
@@ -3655,6 +3724,7 @@ import type { Where } from "./where-types${ext}";
3655
3724
  import type { PaginatedResponse } from "./types/shared${ext}";
3656
3725
  ${typeImports}
3657
3726
  ${includeSpecImport}
3727
+ ${includeResolverImport}
3658
3728
  ${otherTableImports.join(`
3659
3729
  `)}
3660
3730
 
@@ -3845,14 +3915,17 @@ ${hasJsonbColumns ? ` /**
3845
3915
  * @param params.order - Sort direction(s): "asc" or "desc"
3846
3916
  * @param params.limit - Maximum number of records to return (default: 50, max: 1000)
3847
3917
  * @param params.offset - Number of records to skip for pagination
3848
- * @param params.include - Related records to include (see listWith* methods for typed includes)
3849
- * @returns Paginated results with all fields
3918
+ * @param params.include - Related records to include (return type automatically infers included relations)
3919
+ * @returns Paginated results with all fields (and included relations if specified)
3850
3920
  * @example
3851
3921
  * // With JSONB type override:
3852
3922
  * const users = await client.list<{ metadata: Metadata }>({ where: { status: 'active' } });
3923
+ * // With automatic include inference:
3924
+ * const users = await client.list({ include: { posts: true } });
3925
+ * // users[0].posts is automatically typed
3853
3926
  */
3854
- async list<TJsonb extends Partial<Select${Type}> = {}>(params?: {
3855
- include?: ${Type}IncludeSpec;
3927
+ async list<TJsonb extends Partial<Select${Type}> = {}, TInclude extends ${Type}IncludeSpec = {}>(params?: {
3928
+ include?: TInclude;
3856
3929
  limit?: number;
3857
3930
  offset?: number;
3858
3931
  where?: Where<Select${Type}<TJsonb>>;${hasVectorColumns ? `
@@ -3864,7 +3937,7 @@ ${hasJsonbColumns ? ` /**
3864
3937
  };` : ""}
3865
3938
  orderBy?: string | string[];
3866
3939
  order?: "asc" | "desc" | ("asc" | "desc")[];
3867
- }): Promise<PaginatedResponse<Select${Type}<TJsonb>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
3940
+ }): Promise<PaginatedResponse<${Type}WithIncludes<TInclude>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
3868
3941
  async list<TJsonb extends Partial<Select${Type}> = {}>(params?: {
3869
3942
  include?: ${Type}IncludeSpec;
3870
3943
  select?: string[];
@@ -3930,11 +4003,11 @@ ${hasJsonbColumns ? ` /**
3930
4003
  * @param params.order - Sort direction(s): "asc" or "desc"
3931
4004
  * @param params.limit - Maximum number of records to return (default: 50, max: 1000)
3932
4005
  * @param params.offset - Number of records to skip for pagination
3933
- * @param params.include - Related records to include (see listWith* methods for typed includes)
3934
- * @returns Paginated results with all fields
4006
+ * @param params.include - Related records to include (return type automatically infers included relations)
4007
+ * @returns Paginated results with all fields (and included relations if specified)
3935
4008
  */
3936
- async list(params?: {
3937
- include?: ${Type}IncludeSpec;
4009
+ async list<TInclude extends ${Type}IncludeSpec = {}>(params?: {
4010
+ include?: TInclude;
3938
4011
  limit?: number;
3939
4012
  offset?: number;
3940
4013
  where?: Where<Select${Type}>;${hasVectorColumns ? `
@@ -3946,7 +4019,7 @@ ${hasJsonbColumns ? ` /**
3946
4019
  };` : ""}
3947
4020
  orderBy?: string | string[];
3948
4021
  order?: "asc" | "desc" | ("asc" | "desc")[];
3949
- }): Promise<PaginatedResponse<Select${Type}${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
4022
+ }): Promise<PaginatedResponse<${Type}WithIncludes<TInclude>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
3950
4023
  async list(params?: {
3951
4024
  include?: ${Type}IncludeSpec;
3952
4025
  select?: string[];
@@ -5575,7 +5648,7 @@ export * from "./include-spec${ext}";
5575
5648
  }
5576
5649
 
5577
5650
  // src/emit-sdk-bundle.ts
5578
- function emitSdkBundle(clientFiles, clientDir) {
5651
+ function emitSdkBundle(clientFiles, clientDir, version) {
5579
5652
  const files = {};
5580
5653
  for (const file of clientFiles) {
5581
5654
  const normalizedClientDir = clientDir.replace(/\\/g, "/");
@@ -5585,7 +5658,6 @@ function emitSdkBundle(clientFiles, clientDir) {
5585
5658
  files[relativePath] = file.content;
5586
5659
  }
5587
5660
  }
5588
- const version = `1.0.0`;
5589
5661
  return `/**
5590
5662
  * AUTO-GENERATED FILE - DO NOT EDIT
5591
5663
  *
@@ -7059,6 +7131,9 @@ function generateTestCases(table, sampleData, updateData, hasForeignKeys = false
7059
7131
  // src/index.ts
7060
7132
  init_emit_sdk_contract();
7061
7133
  init_utils();
7134
+ var __filename2 = fileURLToPath(import.meta.url);
7135
+ var __dirname2 = dirname2(__filename2);
7136
+ var { version: CLI_VERSION } = JSON.parse(readFileSync(join(__dirname2, "../package.json"), "utf-8"));
7062
7137
  async function generate(configPath) {
7063
7138
  if (!existsSync2(configPath)) {
7064
7139
  throw new Error(`Config file not found: ${configPath}
@@ -7071,6 +7146,7 @@ async function generate(configPath) {
7071
7146
  const rawCfg = module.default || module;
7072
7147
  const normalizedAuth = normalizeAuthConfig(rawCfg.auth);
7073
7148
  const cfg = { ...rawCfg, auth: normalizedAuth };
7149
+ console.log(`\uD83D\uDCE6 postgresdk v${CLI_VERSION}`);
7074
7150
  console.log("\uD83D\uDD0D Introspecting database...");
7075
7151
  const model = await introspect(cfg.connectionString, cfg.schema || "public");
7076
7152
  console.log("\uD83D\uDD17 Building relationship graph...");
@@ -7119,6 +7195,8 @@ async function generate(configPath) {
7119
7195
  const includeSpec = emitIncludeSpec(graph);
7120
7196
  files.push({ path: join(serverDir, "include-spec.ts"), content: includeSpec });
7121
7197
  files.push({ path: join(clientDir, "include-spec.ts"), content: includeSpec });
7198
+ const includeResolver = emitIncludeResolver(graph, cfg.useJsExtensions);
7199
+ files.push({ path: join(clientDir, "include-resolver.ts"), content: includeResolver });
7122
7200
  files.push({ path: join(clientDir, "params", "shared.ts"), content: emitSharedParamsZod() });
7123
7201
  files.push({ path: join(clientDir, "types", "shared.ts"), content: emitSharedTypes() });
7124
7202
  files.push({ path: join(clientDir, "base-client.ts"), content: emitBaseClient() });
@@ -7210,7 +7288,7 @@ async function generate(configPath) {
7210
7288
  });
7211
7289
  files.push({
7212
7290
  path: join(serverDir, "sdk-bundle.ts"),
7213
- content: emitSdkBundle(clientFiles, clientDir)
7291
+ content: emitSdkBundle(clientFiles, clientDir, CLI_VERSION)
7214
7292
  });
7215
7293
  if (generateTests) {
7216
7294
  console.log("\uD83E\uDDEA Generating tests...");
@@ -7286,12 +7364,12 @@ async function generate(configPath) {
7286
7364
  // src/cli.ts
7287
7365
  var import_config2 = __toESM(require_config(), 1);
7288
7366
  import { resolve as resolve3 } from "node:path";
7289
- import { readFileSync as readFileSync2 } from "node:fs";
7290
- import { fileURLToPath } from "node:url";
7291
- import { dirname as dirname3, join as join3 } from "node:path";
7292
- var __filename2 = fileURLToPath(import.meta.url);
7293
- var __dirname2 = dirname3(__filename2);
7294
- var packageJson = JSON.parse(readFileSync2(join3(__dirname2, "../package.json"), "utf-8"));
7367
+ import { readFileSync as readFileSync3 } from "node:fs";
7368
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
7369
+ import { dirname as dirname4, join as join3 } from "node:path";
7370
+ var __filename3 = fileURLToPath2(import.meta.url);
7371
+ var __dirname3 = dirname4(__filename3);
7372
+ var packageJson = JSON.parse(readFileSync3(join3(__dirname3, "../package.json"), "utf-8"));
7295
7373
  var VERSION = packageJson.version;
7296
7374
  var args = process.argv.slice(2);
7297
7375
  var command = args[0];
@@ -0,0 +1,9 @@
1
+ import type { Graph } from "./rel-classify";
2
+ /**
3
+ * Generate TypeScript types that resolve IncludeSpec to actual return types
4
+ *
5
+ * This allows automatic type inference:
6
+ * const result = await sdk.captures.list({ include: { website: true } });
7
+ * // result.data[0].website is typed as SelectWebsites
8
+ */
9
+ export declare function emitIncludeResolver(graph: Graph, useJsExtensions?: boolean): string;
@@ -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
@@ -1701,9 +1701,9 @@ var init_emit_sdk_contract = __esm(() => {
1701
1701
 
1702
1702
  // src/index.ts
1703
1703
  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";
1704
+ import { join, relative, dirname as dirname2 } from "node:path";
1705
+ import { pathToFileURL, fileURLToPath } from "node:url";
1706
+ import { existsSync as existsSync2, readFileSync } from "node:fs";
1707
1707
 
1708
1708
  // src/introspect.ts
1709
1709
  import { Client } from "pg";
@@ -1938,6 +1938,74 @@ function toPascal(s) {
1938
1938
  return s.split(/[_\s-]+/).map((w) => w?.[0] ? w[0].toUpperCase() + w.slice(1) : "").join("");
1939
1939
  }
1940
1940
 
1941
+ // src/emit-include-resolver.ts
1942
+ init_utils();
1943
+ function emitIncludeResolver(graph, useJsExtensions) {
1944
+ const ext = useJsExtensions ? ".js" : "";
1945
+ let out = `/**
1946
+ * AUTO-GENERATED FILE - DO NOT EDIT
1947
+ *
1948
+ * Type helpers for automatic include inference.
1949
+ * These types transform IncludeSpec into the actual return type shape.
1950
+ */
1951
+ `;
1952
+ const tables = Object.keys(graph);
1953
+ for (const table of tables) {
1954
+ out += `import type { Select${pascal(table)} } from "./types/${table}${ext}";
1955
+ `;
1956
+ }
1957
+ for (const table of tables) {
1958
+ out += `import type { ${pascal(table)}IncludeSpec } from "./include-spec${ext}";
1959
+ `;
1960
+ }
1961
+ out += `
1962
+ `;
1963
+ for (const table of tables) {
1964
+ const Type = pascal(table);
1965
+ const edges = graph[table] || {};
1966
+ const edgeEntries = Object.entries(edges);
1967
+ if (edgeEntries.length === 0) {
1968
+ out += `export type ${Type}WithIncludes<TInclude extends ${Type}IncludeSpec> = Select${Type};
1969
+
1970
+ `;
1971
+ continue;
1972
+ }
1973
+ out += `export type ${Type}WithIncludes<TInclude extends ${Type}IncludeSpec> =
1974
+ Select${Type} & {
1975
+ [K in keyof TInclude as TInclude[K] extends false | undefined ? never : K]:`;
1976
+ for (let i = 0;i < edgeEntries.length; i++) {
1977
+ const entry = edgeEntries[i];
1978
+ if (!entry)
1979
+ continue;
1980
+ const [relKey, edge] = entry;
1981
+ const targetType = pascal(edge.target);
1982
+ const isLast = i === edgeEntries.length - 1;
1983
+ out += `
1984
+ K extends '${relKey}' ? `;
1985
+ if (edge.kind === "many") {
1986
+ out += `(
1987
+ TInclude[K] extends { include: infer U extends ${targetType}IncludeSpec }
1988
+ ? Array<${targetType}WithIncludes<U>>
1989
+ : Select${targetType}[]
1990
+ )`;
1991
+ } else {
1992
+ out += `(
1993
+ TInclude[K] extends { include: infer U extends ${targetType}IncludeSpec }
1994
+ ? ${targetType}WithIncludes<U>
1995
+ : Select${targetType}
1996
+ )`;
1997
+ }
1998
+ out += ` :${isLast ? `
1999
+ never` : ""}`;
2000
+ }
2001
+ out += `
2002
+ };
2003
+
2004
+ `;
2005
+ }
2006
+ return out;
2007
+ }
2008
+
1941
2009
  // src/emit-include-builder.ts
1942
2010
  function emitIncludeBuilder(graph, maxDepth) {
1943
2011
  return `/**
@@ -2501,6 +2569,7 @@ function emitClient(table, graph, opts, model) {
2501
2569
  const typeImports = `import type { Insert${Type}, Update${Type}, Select${Type} } from "./types/${table.name}${ext}";`;
2502
2570
  const includeSpecTypes = [table.name, ...Array.from(importedTypes).filter((t) => t !== table.name)];
2503
2571
  const includeSpecImport = `import type { ${includeSpecTypes.map((t) => `${pascal(t)}IncludeSpec`).join(", ")} } from "./include-spec${ext}";`;
2572
+ const includeResolverImport = `import type { ${Type}WithIncludes } from "./include-resolver${ext}";`;
2504
2573
  const otherTableImports = [];
2505
2574
  for (const target of Array.from(importedTypes)) {
2506
2575
  if (target !== table.name) {
@@ -2694,6 +2763,7 @@ import type { Where } from "./where-types${ext}";
2694
2763
  import type { PaginatedResponse } from "./types/shared${ext}";
2695
2764
  ${typeImports}
2696
2765
  ${includeSpecImport}
2766
+ ${includeResolverImport}
2697
2767
  ${otherTableImports.join(`
2698
2768
  `)}
2699
2769
 
@@ -2884,14 +2954,17 @@ ${hasJsonbColumns ? ` /**
2884
2954
  * @param params.order - Sort direction(s): "asc" or "desc"
2885
2955
  * @param params.limit - Maximum number of records to return (default: 50, max: 1000)
2886
2956
  * @param params.offset - Number of records to skip for pagination
2887
- * @param params.include - Related records to include (see listWith* methods for typed includes)
2888
- * @returns Paginated results with all fields
2957
+ * @param params.include - Related records to include (return type automatically infers included relations)
2958
+ * @returns Paginated results with all fields (and included relations if specified)
2889
2959
  * @example
2890
2960
  * // With JSONB type override:
2891
2961
  * const users = await client.list<{ metadata: Metadata }>({ where: { status: 'active' } });
2962
+ * // With automatic include inference:
2963
+ * const users = await client.list({ include: { posts: true } });
2964
+ * // users[0].posts is automatically typed
2892
2965
  */
2893
- async list<TJsonb extends Partial<Select${Type}> = {}>(params?: {
2894
- include?: ${Type}IncludeSpec;
2966
+ async list<TJsonb extends Partial<Select${Type}> = {}, TInclude extends ${Type}IncludeSpec = {}>(params?: {
2967
+ include?: TInclude;
2895
2968
  limit?: number;
2896
2969
  offset?: number;
2897
2970
  where?: Where<Select${Type}<TJsonb>>;${hasVectorColumns ? `
@@ -2903,7 +2976,7 @@ ${hasJsonbColumns ? ` /**
2903
2976
  };` : ""}
2904
2977
  orderBy?: string | string[];
2905
2978
  order?: "asc" | "desc" | ("asc" | "desc")[];
2906
- }): Promise<PaginatedResponse<Select${Type}<TJsonb>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
2979
+ }): Promise<PaginatedResponse<${Type}WithIncludes<TInclude>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
2907
2980
  async list<TJsonb extends Partial<Select${Type}> = {}>(params?: {
2908
2981
  include?: ${Type}IncludeSpec;
2909
2982
  select?: string[];
@@ -2969,11 +3042,11 @@ ${hasJsonbColumns ? ` /**
2969
3042
  * @param params.order - Sort direction(s): "asc" or "desc"
2970
3043
  * @param params.limit - Maximum number of records to return (default: 50, max: 1000)
2971
3044
  * @param params.offset - Number of records to skip for pagination
2972
- * @param params.include - Related records to include (see listWith* methods for typed includes)
2973
- * @returns Paginated results with all fields
3045
+ * @param params.include - Related records to include (return type automatically infers included relations)
3046
+ * @returns Paginated results with all fields (and included relations if specified)
2974
3047
  */
2975
- async list(params?: {
2976
- include?: ${Type}IncludeSpec;
3048
+ async list<TInclude extends ${Type}IncludeSpec = {}>(params?: {
3049
+ include?: TInclude;
2977
3050
  limit?: number;
2978
3051
  offset?: number;
2979
3052
  where?: Where<Select${Type}>;${hasVectorColumns ? `
@@ -2985,7 +3058,7 @@ ${hasJsonbColumns ? ` /**
2985
3058
  };` : ""}
2986
3059
  orderBy?: string | string[];
2987
3060
  order?: "asc" | "desc" | ("asc" | "desc")[];
2988
- }): Promise<PaginatedResponse<Select${Type}${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
3061
+ }): Promise<PaginatedResponse<${Type}WithIncludes<TInclude>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
2989
3062
  async list(params?: {
2990
3063
  include?: ${Type}IncludeSpec;
2991
3064
  select?: string[];
@@ -4614,7 +4687,7 @@ export * from "./include-spec${ext}";
4614
4687
  }
4615
4688
 
4616
4689
  // src/emit-sdk-bundle.ts
4617
- function emitSdkBundle(clientFiles, clientDir) {
4690
+ function emitSdkBundle(clientFiles, clientDir, version) {
4618
4691
  const files = {};
4619
4692
  for (const file of clientFiles) {
4620
4693
  const normalizedClientDir = clientDir.replace(/\\/g, "/");
@@ -4624,7 +4697,6 @@ function emitSdkBundle(clientFiles, clientDir) {
4624
4697
  files[relativePath] = file.content;
4625
4698
  }
4626
4699
  }
4627
- const version = `1.0.0`;
4628
4700
  return `/**
4629
4701
  * AUTO-GENERATED FILE - DO NOT EDIT
4630
4702
  *
@@ -6098,6 +6170,9 @@ function generateTestCases(table, sampleData, updateData, hasForeignKeys = false
6098
6170
  // src/index.ts
6099
6171
  init_emit_sdk_contract();
6100
6172
  init_utils();
6173
+ var __filename2 = fileURLToPath(import.meta.url);
6174
+ var __dirname2 = dirname2(__filename2);
6175
+ var { version: CLI_VERSION } = JSON.parse(readFileSync(join(__dirname2, "../package.json"), "utf-8"));
6101
6176
  async function generate(configPath) {
6102
6177
  if (!existsSync2(configPath)) {
6103
6178
  throw new Error(`Config file not found: ${configPath}
@@ -6110,6 +6185,7 @@ async function generate(configPath) {
6110
6185
  const rawCfg = module.default || module;
6111
6186
  const normalizedAuth = normalizeAuthConfig(rawCfg.auth);
6112
6187
  const cfg = { ...rawCfg, auth: normalizedAuth };
6188
+ console.log(`\uD83D\uDCE6 postgresdk v${CLI_VERSION}`);
6113
6189
  console.log("\uD83D\uDD0D Introspecting database...");
6114
6190
  const model = await introspect(cfg.connectionString, cfg.schema || "public");
6115
6191
  console.log("\uD83D\uDD17 Building relationship graph...");
@@ -6158,6 +6234,8 @@ async function generate(configPath) {
6158
6234
  const includeSpec = emitIncludeSpec(graph);
6159
6235
  files.push({ path: join(serverDir, "include-spec.ts"), content: includeSpec });
6160
6236
  files.push({ path: join(clientDir, "include-spec.ts"), content: includeSpec });
6237
+ const includeResolver = emitIncludeResolver(graph, cfg.useJsExtensions);
6238
+ files.push({ path: join(clientDir, "include-resolver.ts"), content: includeResolver });
6161
6239
  files.push({ path: join(clientDir, "params", "shared.ts"), content: emitSharedParamsZod() });
6162
6240
  files.push({ path: join(clientDir, "types", "shared.ts"), content: emitSharedTypes() });
6163
6241
  files.push({ path: join(clientDir, "base-client.ts"), content: emitBaseClient() });
@@ -6249,7 +6327,7 @@ async function generate(configPath) {
6249
6327
  });
6250
6328
  files.push({
6251
6329
  path: join(serverDir, "sdk-bundle.ts"),
6252
- content: emitSdkBundle(clientFiles, clientDir)
6330
+ content: emitSdkBundle(clientFiles, clientDir, CLI_VERSION)
6253
6331
  });
6254
6332
  if (generateTests) {
6255
6333
  console.log("\uD83E\uDDEA Generating tests...");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postgresdk",
3
- "version": "0.18.9",
3
+ "version": "0.18.11",
4
4
  "description": "Generate a typed server/client SDK from a Postgres schema (includes, Zod, Hono).",
5
5
  "type": "module",
6
6
  "bin": {