postgresdk 0.7.6 → 0.9.0

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
@@ -1914,18 +1914,214 @@ ${hasAuth ? `
1914
1914
 
1915
1915
  // src/emit-client.ts
1916
1916
  init_utils();
1917
- function emitClient(table, useJsExtensions) {
1917
+
1918
+ // src/emit-include-methods.ts
1919
+ init_utils();
1920
+ function isJunctionTable(table) {
1921
+ if (!table.name.includes("_"))
1922
+ return false;
1923
+ const fkColumns = new Set(table.fks.flatMap((fk) => fk.from));
1924
+ const nonPkColumns = table.columns.filter((c) => !table.pk.includes(c.name));
1925
+ return nonPkColumns.every((c) => fkColumns.has(c.name));
1926
+ }
1927
+ function pathToMethodSuffix(path) {
1928
+ return "With" + path.map((p) => pascal(p)).join("And");
1929
+ }
1930
+ function buildReturnType(baseTable, path, isMany, targets, graph) {
1931
+ const BaseType = `Select${pascal(baseTable)}`;
1932
+ if (path.length === 0)
1933
+ return BaseType;
1934
+ let type = BaseType;
1935
+ let currentTable = baseTable;
1936
+ const parts = [];
1937
+ for (let i = 0;i < path.length; i++) {
1938
+ const key = path[i];
1939
+ const target = targets[i];
1940
+ if (!key || !target)
1941
+ continue;
1942
+ const targetType = `Select${pascal(target)}`;
1943
+ if (i === 0) {
1944
+ parts.push(`${key}: ${isMany[i] ? `${targetType}[]` : targetType}`);
1945
+ } else {
1946
+ let nestedType = targetType;
1947
+ for (let j = i;j < path.length; j++) {
1948
+ if (j > i) {
1949
+ const nestedKey = path[j];
1950
+ const nestedTarget = targets[j];
1951
+ if (!nestedKey || !nestedTarget)
1952
+ continue;
1953
+ const nestedTargetType = `Select${pascal(nestedTarget)}`;
1954
+ nestedType = `${nestedType} & { ${nestedKey}: ${isMany[j] ? `${nestedTargetType}[]` : nestedTargetType} }`;
1955
+ }
1956
+ }
1957
+ const prevKey = path[i - 1];
1958
+ const prevTarget = targets[i - 1];
1959
+ if (prevKey && prevTarget) {
1960
+ parts[parts.length - 1] = `${prevKey}: ${isMany[i - 1] ? `(Select${pascal(prevTarget)} & { ${key}: ${isMany[i] ? `${targetType}[]` : targetType} })[]` : `Select${pascal(prevTarget)} & { ${key}: ${isMany[i] ? `${targetType}[]` : targetType} }`}`;
1961
+ }
1962
+ break;
1963
+ }
1964
+ }
1965
+ return `${type} & { ${parts.join("; ")} }`;
1966
+ }
1967
+ function buildIncludeSpec(path) {
1968
+ if (path.length === 0)
1969
+ return {};
1970
+ if (path.length === 1)
1971
+ return { [path[0]]: true };
1972
+ let spec = true;
1973
+ for (let i = path.length - 1;i > 0; i--) {
1974
+ const key = path[i];
1975
+ if (!key)
1976
+ continue;
1977
+ spec = { [key]: spec };
1978
+ }
1979
+ const rootKey = path[0];
1980
+ return rootKey ? { [rootKey]: spec } : {};
1981
+ }
1982
+ function generateIncludeMethods(table, graph, opts, allTables) {
1983
+ const methods = [];
1984
+ const baseTableName = table.name;
1985
+ if (opts.skipJunctionTables && isJunctionTable(table)) {
1986
+ return methods;
1987
+ }
1988
+ const edges = graph[baseTableName] || {};
1989
+ function explore(currentTable, path, isMany, targets, visited, depth) {
1990
+ if (depth > opts.maxDepth)
1991
+ return;
1992
+ const currentEdges = graph[currentTable] || {};
1993
+ for (const [key, edge] of Object.entries(currentEdges)) {
1994
+ if (visited.has(edge.target))
1995
+ continue;
1996
+ if (opts.skipJunctionTables && allTables) {
1997
+ const targetTable = allTables.find((t) => t.name === edge.target);
1998
+ if (targetTable && isJunctionTable(targetTable)) {
1999
+ continue;
2000
+ }
2001
+ }
2002
+ const newPath = [...path, key];
2003
+ const newIsMany = [...isMany, edge.kind === "many"];
2004
+ const newTargets = [...targets, edge.target];
2005
+ const methodSuffix = pathToMethodSuffix(newPath);
2006
+ methods.push({
2007
+ name: `list${methodSuffix}`,
2008
+ path: newPath,
2009
+ isMany: newIsMany,
2010
+ targets: newTargets,
2011
+ returnType: `(${buildReturnType(baseTableName, newPath, newIsMany, newTargets, graph)})[]`,
2012
+ includeSpec: buildIncludeSpec(newPath)
2013
+ });
2014
+ methods.push({
2015
+ name: `getByPk${methodSuffix}`,
2016
+ path: newPath,
2017
+ isMany: newIsMany,
2018
+ targets: newTargets,
2019
+ returnType: `${buildReturnType(baseTableName, newPath, newIsMany, newTargets, graph)} | null`,
2020
+ includeSpec: buildIncludeSpec(newPath)
2021
+ });
2022
+ explore(edge.target, newPath, newIsMany, newTargets, new Set([...visited, edge.target]), depth + 1);
2023
+ }
2024
+ if (depth === 1 && Object.keys(currentEdges).length > 1 && Object.keys(currentEdges).length <= 3) {
2025
+ const edgeEntries = Object.entries(currentEdges);
2026
+ if (edgeEntries.length >= 2) {
2027
+ for (let i = 0;i < edgeEntries.length - 1; i++) {
2028
+ for (let j = i + 1;j < edgeEntries.length; j++) {
2029
+ const entry1 = edgeEntries[i];
2030
+ const entry2 = edgeEntries[j];
2031
+ if (!entry1 || !entry2)
2032
+ continue;
2033
+ const [key1, edge1] = entry1;
2034
+ const [key2, edge2] = entry2;
2035
+ if (opts.skipJunctionTables && (edge1.target.includes("_") || edge2.target.includes("_"))) {
2036
+ continue;
2037
+ }
2038
+ const combinedPath = [key1, key2];
2039
+ const combinedSuffix = `With${pascal(key1)}And${pascal(key2)}`;
2040
+ const type1 = `${key1}: ${edge1.kind === "many" ? `Select${pascal(edge1.target)}[]` : `Select${pascal(edge1.target)}`}`;
2041
+ const type2 = `${key2}: ${edge2.kind === "many" ? `Select${pascal(edge2.target)}[]` : `Select${pascal(edge2.target)}`}`;
2042
+ methods.push({
2043
+ name: `list${combinedSuffix}`,
2044
+ path: combinedPath,
2045
+ isMany: [edge1.kind === "many", edge2.kind === "many"],
2046
+ targets: [edge1.target, edge2.target],
2047
+ returnType: `(Select${pascal(baseTableName)} & { ${type1}; ${type2} })[]`,
2048
+ includeSpec: { [key1]: true, [key2]: true }
2049
+ });
2050
+ methods.push({
2051
+ name: `getByPk${combinedSuffix}`,
2052
+ path: combinedPath,
2053
+ isMany: [edge1.kind === "many", edge2.kind === "many"],
2054
+ targets: [edge1.target, edge2.target],
2055
+ returnType: `(Select${pascal(baseTableName)} & { ${type1}; ${type2} }) | null`,
2056
+ includeSpec: { [key1]: true, [key2]: true }
2057
+ });
2058
+ }
2059
+ }
2060
+ }
2061
+ }
2062
+ }
2063
+ explore(baseTableName, [], [], [], new Set([baseTableName]), 1);
2064
+ return methods;
2065
+ }
2066
+
2067
+ // src/emit-client.ts
2068
+ function emitClient(table, graph, opts, model) {
1918
2069
  const Type = pascal(table.name);
1919
- const ext = useJsExtensions ? ".js" : "";
2070
+ const ext = opts.useJsExtensions ? ".js" : "";
1920
2071
  const pkCols = Array.isArray(table.pk) ? table.pk : table.pk ? [table.pk] : [];
1921
2072
  const safePk = pkCols.length ? pkCols : ["id"];
1922
2073
  const hasCompositePk = safePk.length > 1;
1923
2074
  const pkType = hasCompositePk ? `{ ${safePk.map((c) => `${c}: string`).join("; ")} }` : `string`;
1924
2075
  const pkPathExpr = hasCompositePk ? safePk.map((c) => `pk.${c}`).join(` + "/" + `) : `pk`;
2076
+ const allTables = model ? Object.values(model.tables) : undefined;
2077
+ const includeMethods = generateIncludeMethods(table, graph, {
2078
+ maxDepth: opts.includeMethodsDepth ?? 2,
2079
+ skipJunctionTables: opts.skipJunctionTables ?? true
2080
+ }, allTables);
2081
+ const importedTypes = new Set;
2082
+ importedTypes.add(table.name);
2083
+ for (const method of includeMethods) {
2084
+ for (const target of method.targets) {
2085
+ importedTypes.add(target);
2086
+ }
2087
+ }
2088
+ const typeImports = `import type { Insert${Type}, Update${Type}, Select${Type} } from "./types/${table.name}${ext}";`;
2089
+ const otherTableImports = [];
2090
+ for (const target of Array.from(importedTypes)) {
2091
+ if (target !== table.name) {
2092
+ otherTableImports.push(`import type { Select${pascal(target)} } from "./types/${target}${ext}";`);
2093
+ }
2094
+ }
2095
+ let includeMethodsCode = "";
2096
+ for (const method of includeMethods) {
2097
+ const isGetByPk = method.name.startsWith("getByPk");
2098
+ const baseParams = isGetByPk ? "" : `params?: Omit<{ limit?: number; offset?: number; where?: any; orderBy?: string; order?: "asc" | "desc"; }, "include">`;
2099
+ if (isGetByPk) {
2100
+ const pkWhere = hasCompositePk ? `{ ${safePk.map((col) => `${col}: pk.${col}`).join(", ")} }` : `{ ${safePk[0] || "id"}: pk }`;
2101
+ const baseReturnType = method.returnType.replace(" | null", "");
2102
+ includeMethodsCode += `
2103
+ async ${method.name}(pk: ${pkType}): Promise<${method.returnType}> {
2104
+ const results = await this.post<${baseReturnType}[]>(\`\${this.resource}/list\`, {
2105
+ where: ${pkWhere},
2106
+ include: ${JSON.stringify(method.includeSpec)},
2107
+ limit: 1
2108
+ });
2109
+ return (results[0] as ${baseReturnType}) ?? null;
2110
+ }
2111
+ `;
2112
+ } else {
2113
+ includeMethodsCode += `
2114
+ async ${method.name}(${baseParams}): Promise<${method.returnType}> {
2115
+ return this.post<${method.returnType}>(\`\${this.resource}/list\`, { ...params, include: ${JSON.stringify(method.includeSpec)} });
2116
+ }
2117
+ `;
2118
+ }
2119
+ }
1925
2120
  return `/* Generated. Do not edit. */
1926
2121
  import { BaseClient } from "./base-client${ext}";
1927
- import type { ${Type}IncludeSpec } from "./include-spec${ext}";
1928
- import type { Insert${Type}, Update${Type}, Select${Type} } from "./types/${table.name}${ext}";
2122
+ ${typeImports}
2123
+ ${otherTableImports.join(`
2124
+ `)}
1929
2125
 
1930
2126
  /**
1931
2127
  * Client for ${table.name} table operations
@@ -1943,7 +2139,6 @@ export class ${Type}Client extends BaseClient {
1943
2139
  }
1944
2140
 
1945
2141
  async list(params?: {
1946
- include?: ${Type}IncludeSpec;
1947
2142
  limit?: number;
1948
2143
  offset?: number;
1949
2144
  where?: any;
@@ -1962,7 +2157,7 @@ export class ${Type}Client extends BaseClient {
1962
2157
  const path = ${pkPathExpr};
1963
2158
  return this.del<Select${Type} | null>(\`\${this.resource}/\${path}\`);
1964
2159
  }
1965
- }
2160
+ ${includeMethodsCode}}
1966
2161
  `;
1967
2162
  }
1968
2163
  function emitClientIndex(tables, useJsExtensions) {
@@ -2006,8 +2201,6 @@ export type { AuthConfig, HeaderMap, AuthHeadersProvider } from "./base-client${
2006
2201
 
2007
2202
  `;
2008
2203
  out += `export { BaseClient } from "./base-client${ext}";
2009
- `;
2010
- out += `export * from "./include-spec${ext}";
2011
2204
  `;
2012
2205
  out += `
2013
2206
  // Zod schemas for form validation
@@ -3084,12 +3277,12 @@ function emitTableTest(table, model, clientPath, framework = "vitest") {
3084
3277
  const Type = pascal(table.name);
3085
3278
  const tableName = table.name;
3086
3279
  const imports = getFrameworkImports(framework);
3087
- const isJunctionTable = table.pk.length > 1;
3280
+ const isJunctionTable2 = table.pk.length > 1;
3088
3281
  const hasForeignKeys = table.fks.length > 0;
3089
3282
  const foreignKeySetup = hasForeignKeys ? generateForeignKeySetup(table, model, clientPath) : null;
3090
3283
  const sampleData = generateSampleDataFromSchema(table, hasForeignKeys);
3091
3284
  const updateData = generateUpdateDataFromSchema(table);
3092
- if (isJunctionTable) {
3285
+ if (isJunctionTable2) {
3093
3286
  return `${imports}
3094
3287
  import { SDK } from '${clientPath}';
3095
3288
  import type { Insert${Type}, Update${Type}, Select${Type} } from '${clientPath}/types/${tableName}';
@@ -3893,11 +4086,11 @@ async function generate(configPath) {
3893
4086
  files.push({ path: join(clientDir, "base-client.ts"), content: emitBaseClient() });
3894
4087
  files.push({
3895
4088
  path: join(serverDir, "include-builder.ts"),
3896
- content: emitIncludeBuilder(graph, cfg.includeDepthLimit || 3)
4089
+ content: emitIncludeBuilder(graph, cfg.includeMethodsDepth || 2)
3897
4090
  });
3898
4091
  files.push({
3899
4092
  path: join(serverDir, "include-loader.ts"),
3900
- content: emitIncludeLoader(graph, model, cfg.includeDepthLimit || 3, cfg.useJsExtensions)
4093
+ content: emitIncludeLoader(graph, model, cfg.includeMethodsDepth || 2, cfg.useJsExtensions)
3901
4094
  });
3902
4095
  files.push({ path: join(serverDir, "logger.ts"), content: emitLogger() });
3903
4096
  if (normalizedAuth?.strategy && normalizedAuth.strategy !== "none") {
@@ -3923,7 +4116,7 @@ async function generate(configPath) {
3923
4116
  if (serverFramework === "hono") {
3924
4117
  routeContent = emitHonoRoutes(table, graph, {
3925
4118
  softDeleteColumn: cfg.softDeleteColumn || null,
3926
- includeDepthLimit: cfg.includeDepthLimit || 3,
4119
+ includeDepthLimit: cfg.includeMethodsDepth || 2,
3927
4120
  authStrategy: normalizedAuth?.strategy,
3928
4121
  useJsExtensions: cfg.useJsExtensions
3929
4122
  });
@@ -3936,7 +4129,11 @@ async function generate(configPath) {
3936
4129
  });
3937
4130
  files.push({
3938
4131
  path: join(clientDir, `${table.name}.ts`),
3939
- content: emitClient(table, cfg.useJsExtensionsClient)
4132
+ content: emitClient(table, graph, {
4133
+ useJsExtensions: cfg.useJsExtensionsClient,
4134
+ includeMethodsDepth: cfg.includeMethodsDepth ?? 2,
4135
+ skipJunctionTables: cfg.skipJunctionTables ?? true
4136
+ }, model)
3940
4137
  });
3941
4138
  }
3942
4139
  files.push({
@@ -1,3 +1,8 @@
1
- import type { Table } from "./introspect";
2
- export declare function emitClient(table: Table, useJsExtensions?: boolean): string;
1
+ import type { Table, Model } from "./introspect";
2
+ import type { Graph } from "./rel-classify";
3
+ export declare function emitClient(table: Table, graph: Graph, opts: {
4
+ useJsExtensions?: boolean;
5
+ includeMethodsDepth?: number;
6
+ skipJunctionTables?: boolean;
7
+ }, model?: Model): string;
3
8
  export declare function emitClientIndex(tables: Table[], useJsExtensions?: boolean): string;
@@ -0,0 +1,17 @@
1
+ import type { Table } from "./introspect";
2
+ import type { Graph } from "./rel-classify";
3
+ export type IncludeMethod = {
4
+ name: string;
5
+ path: string[];
6
+ isMany: boolean[];
7
+ targets: string[];
8
+ returnType: string;
9
+ includeSpec: any;
10
+ };
11
+ /**
12
+ * Generate all include methods for a table
13
+ */
14
+ export declare function generateIncludeMethods(table: Table, graph: Graph, opts: {
15
+ maxDepth: number;
16
+ skipJunctionTables: boolean;
17
+ }, allTables?: Table[]): IncludeMethod[];
package/dist/index.js CHANGED
@@ -1651,18 +1651,214 @@ ${hasAuth ? `
1651
1651
 
1652
1652
  // src/emit-client.ts
1653
1653
  init_utils();
1654
- function emitClient(table, useJsExtensions) {
1654
+
1655
+ // src/emit-include-methods.ts
1656
+ init_utils();
1657
+ function isJunctionTable(table) {
1658
+ if (!table.name.includes("_"))
1659
+ return false;
1660
+ const fkColumns = new Set(table.fks.flatMap((fk) => fk.from));
1661
+ const nonPkColumns = table.columns.filter((c) => !table.pk.includes(c.name));
1662
+ return nonPkColumns.every((c) => fkColumns.has(c.name));
1663
+ }
1664
+ function pathToMethodSuffix(path) {
1665
+ return "With" + path.map((p) => pascal(p)).join("And");
1666
+ }
1667
+ function buildReturnType(baseTable, path, isMany, targets, graph) {
1668
+ const BaseType = `Select${pascal(baseTable)}`;
1669
+ if (path.length === 0)
1670
+ return BaseType;
1671
+ let type = BaseType;
1672
+ let currentTable = baseTable;
1673
+ const parts = [];
1674
+ for (let i = 0;i < path.length; i++) {
1675
+ const key = path[i];
1676
+ const target = targets[i];
1677
+ if (!key || !target)
1678
+ continue;
1679
+ const targetType = `Select${pascal(target)}`;
1680
+ if (i === 0) {
1681
+ parts.push(`${key}: ${isMany[i] ? `${targetType}[]` : targetType}`);
1682
+ } else {
1683
+ let nestedType = targetType;
1684
+ for (let j = i;j < path.length; j++) {
1685
+ if (j > i) {
1686
+ const nestedKey = path[j];
1687
+ const nestedTarget = targets[j];
1688
+ if (!nestedKey || !nestedTarget)
1689
+ continue;
1690
+ const nestedTargetType = `Select${pascal(nestedTarget)}`;
1691
+ nestedType = `${nestedType} & { ${nestedKey}: ${isMany[j] ? `${nestedTargetType}[]` : nestedTargetType} }`;
1692
+ }
1693
+ }
1694
+ const prevKey = path[i - 1];
1695
+ const prevTarget = targets[i - 1];
1696
+ if (prevKey && prevTarget) {
1697
+ parts[parts.length - 1] = `${prevKey}: ${isMany[i - 1] ? `(Select${pascal(prevTarget)} & { ${key}: ${isMany[i] ? `${targetType}[]` : targetType} })[]` : `Select${pascal(prevTarget)} & { ${key}: ${isMany[i] ? `${targetType}[]` : targetType} }`}`;
1698
+ }
1699
+ break;
1700
+ }
1701
+ }
1702
+ return `${type} & { ${parts.join("; ")} }`;
1703
+ }
1704
+ function buildIncludeSpec(path) {
1705
+ if (path.length === 0)
1706
+ return {};
1707
+ if (path.length === 1)
1708
+ return { [path[0]]: true };
1709
+ let spec = true;
1710
+ for (let i = path.length - 1;i > 0; i--) {
1711
+ const key = path[i];
1712
+ if (!key)
1713
+ continue;
1714
+ spec = { [key]: spec };
1715
+ }
1716
+ const rootKey = path[0];
1717
+ return rootKey ? { [rootKey]: spec } : {};
1718
+ }
1719
+ function generateIncludeMethods(table, graph, opts, allTables) {
1720
+ const methods = [];
1721
+ const baseTableName = table.name;
1722
+ if (opts.skipJunctionTables && isJunctionTable(table)) {
1723
+ return methods;
1724
+ }
1725
+ const edges = graph[baseTableName] || {};
1726
+ function explore(currentTable, path, isMany, targets, visited, depth) {
1727
+ if (depth > opts.maxDepth)
1728
+ return;
1729
+ const currentEdges = graph[currentTable] || {};
1730
+ for (const [key, edge] of Object.entries(currentEdges)) {
1731
+ if (visited.has(edge.target))
1732
+ continue;
1733
+ if (opts.skipJunctionTables && allTables) {
1734
+ const targetTable = allTables.find((t) => t.name === edge.target);
1735
+ if (targetTable && isJunctionTable(targetTable)) {
1736
+ continue;
1737
+ }
1738
+ }
1739
+ const newPath = [...path, key];
1740
+ const newIsMany = [...isMany, edge.kind === "many"];
1741
+ const newTargets = [...targets, edge.target];
1742
+ const methodSuffix = pathToMethodSuffix(newPath);
1743
+ methods.push({
1744
+ name: `list${methodSuffix}`,
1745
+ path: newPath,
1746
+ isMany: newIsMany,
1747
+ targets: newTargets,
1748
+ returnType: `(${buildReturnType(baseTableName, newPath, newIsMany, newTargets, graph)})[]`,
1749
+ includeSpec: buildIncludeSpec(newPath)
1750
+ });
1751
+ methods.push({
1752
+ name: `getByPk${methodSuffix}`,
1753
+ path: newPath,
1754
+ isMany: newIsMany,
1755
+ targets: newTargets,
1756
+ returnType: `${buildReturnType(baseTableName, newPath, newIsMany, newTargets, graph)} | null`,
1757
+ includeSpec: buildIncludeSpec(newPath)
1758
+ });
1759
+ explore(edge.target, newPath, newIsMany, newTargets, new Set([...visited, edge.target]), depth + 1);
1760
+ }
1761
+ if (depth === 1 && Object.keys(currentEdges).length > 1 && Object.keys(currentEdges).length <= 3) {
1762
+ const edgeEntries = Object.entries(currentEdges);
1763
+ if (edgeEntries.length >= 2) {
1764
+ for (let i = 0;i < edgeEntries.length - 1; i++) {
1765
+ for (let j = i + 1;j < edgeEntries.length; j++) {
1766
+ const entry1 = edgeEntries[i];
1767
+ const entry2 = edgeEntries[j];
1768
+ if (!entry1 || !entry2)
1769
+ continue;
1770
+ const [key1, edge1] = entry1;
1771
+ const [key2, edge2] = entry2;
1772
+ if (opts.skipJunctionTables && (edge1.target.includes("_") || edge2.target.includes("_"))) {
1773
+ continue;
1774
+ }
1775
+ const combinedPath = [key1, key2];
1776
+ const combinedSuffix = `With${pascal(key1)}And${pascal(key2)}`;
1777
+ const type1 = `${key1}: ${edge1.kind === "many" ? `Select${pascal(edge1.target)}[]` : `Select${pascal(edge1.target)}`}`;
1778
+ const type2 = `${key2}: ${edge2.kind === "many" ? `Select${pascal(edge2.target)}[]` : `Select${pascal(edge2.target)}`}`;
1779
+ methods.push({
1780
+ name: `list${combinedSuffix}`,
1781
+ path: combinedPath,
1782
+ isMany: [edge1.kind === "many", edge2.kind === "many"],
1783
+ targets: [edge1.target, edge2.target],
1784
+ returnType: `(Select${pascal(baseTableName)} & { ${type1}; ${type2} })[]`,
1785
+ includeSpec: { [key1]: true, [key2]: true }
1786
+ });
1787
+ methods.push({
1788
+ name: `getByPk${combinedSuffix}`,
1789
+ path: combinedPath,
1790
+ isMany: [edge1.kind === "many", edge2.kind === "many"],
1791
+ targets: [edge1.target, edge2.target],
1792
+ returnType: `(Select${pascal(baseTableName)} & { ${type1}; ${type2} }) | null`,
1793
+ includeSpec: { [key1]: true, [key2]: true }
1794
+ });
1795
+ }
1796
+ }
1797
+ }
1798
+ }
1799
+ }
1800
+ explore(baseTableName, [], [], [], new Set([baseTableName]), 1);
1801
+ return methods;
1802
+ }
1803
+
1804
+ // src/emit-client.ts
1805
+ function emitClient(table, graph, opts, model) {
1655
1806
  const Type = pascal(table.name);
1656
- const ext = useJsExtensions ? ".js" : "";
1807
+ const ext = opts.useJsExtensions ? ".js" : "";
1657
1808
  const pkCols = Array.isArray(table.pk) ? table.pk : table.pk ? [table.pk] : [];
1658
1809
  const safePk = pkCols.length ? pkCols : ["id"];
1659
1810
  const hasCompositePk = safePk.length > 1;
1660
1811
  const pkType = hasCompositePk ? `{ ${safePk.map((c) => `${c}: string`).join("; ")} }` : `string`;
1661
1812
  const pkPathExpr = hasCompositePk ? safePk.map((c) => `pk.${c}`).join(` + "/" + `) : `pk`;
1813
+ const allTables = model ? Object.values(model.tables) : undefined;
1814
+ const includeMethods = generateIncludeMethods(table, graph, {
1815
+ maxDepth: opts.includeMethodsDepth ?? 2,
1816
+ skipJunctionTables: opts.skipJunctionTables ?? true
1817
+ }, allTables);
1818
+ const importedTypes = new Set;
1819
+ importedTypes.add(table.name);
1820
+ for (const method of includeMethods) {
1821
+ for (const target of method.targets) {
1822
+ importedTypes.add(target);
1823
+ }
1824
+ }
1825
+ const typeImports = `import type { Insert${Type}, Update${Type}, Select${Type} } from "./types/${table.name}${ext}";`;
1826
+ const otherTableImports = [];
1827
+ for (const target of Array.from(importedTypes)) {
1828
+ if (target !== table.name) {
1829
+ otherTableImports.push(`import type { Select${pascal(target)} } from "./types/${target}${ext}";`);
1830
+ }
1831
+ }
1832
+ let includeMethodsCode = "";
1833
+ for (const method of includeMethods) {
1834
+ const isGetByPk = method.name.startsWith("getByPk");
1835
+ const baseParams = isGetByPk ? "" : `params?: Omit<{ limit?: number; offset?: number; where?: any; orderBy?: string; order?: "asc" | "desc"; }, "include">`;
1836
+ if (isGetByPk) {
1837
+ const pkWhere = hasCompositePk ? `{ ${safePk.map((col) => `${col}: pk.${col}`).join(", ")} }` : `{ ${safePk[0] || "id"}: pk }`;
1838
+ const baseReturnType = method.returnType.replace(" | null", "");
1839
+ includeMethodsCode += `
1840
+ async ${method.name}(pk: ${pkType}): Promise<${method.returnType}> {
1841
+ const results = await this.post<${baseReturnType}[]>(\`\${this.resource}/list\`, {
1842
+ where: ${pkWhere},
1843
+ include: ${JSON.stringify(method.includeSpec)},
1844
+ limit: 1
1845
+ });
1846
+ return (results[0] as ${baseReturnType}) ?? null;
1847
+ }
1848
+ `;
1849
+ } else {
1850
+ includeMethodsCode += `
1851
+ async ${method.name}(${baseParams}): Promise<${method.returnType}> {
1852
+ return this.post<${method.returnType}>(\`\${this.resource}/list\`, { ...params, include: ${JSON.stringify(method.includeSpec)} });
1853
+ }
1854
+ `;
1855
+ }
1856
+ }
1662
1857
  return `/* Generated. Do not edit. */
1663
1858
  import { BaseClient } from "./base-client${ext}";
1664
- import type { ${Type}IncludeSpec } from "./include-spec${ext}";
1665
- import type { Insert${Type}, Update${Type}, Select${Type} } from "./types/${table.name}${ext}";
1859
+ ${typeImports}
1860
+ ${otherTableImports.join(`
1861
+ `)}
1666
1862
 
1667
1863
  /**
1668
1864
  * Client for ${table.name} table operations
@@ -1680,7 +1876,6 @@ export class ${Type}Client extends BaseClient {
1680
1876
  }
1681
1877
 
1682
1878
  async list(params?: {
1683
- include?: ${Type}IncludeSpec;
1684
1879
  limit?: number;
1685
1880
  offset?: number;
1686
1881
  where?: any;
@@ -1699,7 +1894,7 @@ export class ${Type}Client extends BaseClient {
1699
1894
  const path = ${pkPathExpr};
1700
1895
  return this.del<Select${Type} | null>(\`\${this.resource}/\${path}\`);
1701
1896
  }
1702
- }
1897
+ ${includeMethodsCode}}
1703
1898
  `;
1704
1899
  }
1705
1900
  function emitClientIndex(tables, useJsExtensions) {
@@ -1743,8 +1938,6 @@ export type { AuthConfig, HeaderMap, AuthHeadersProvider } from "./base-client${
1743
1938
 
1744
1939
  `;
1745
1940
  out += `export { BaseClient } from "./base-client${ext}";
1746
- `;
1747
- out += `export * from "./include-spec${ext}";
1748
1941
  `;
1749
1942
  out += `
1750
1943
  // Zod schemas for form validation
@@ -2821,12 +3014,12 @@ function emitTableTest(table, model, clientPath, framework = "vitest") {
2821
3014
  const Type = pascal(table.name);
2822
3015
  const tableName = table.name;
2823
3016
  const imports = getFrameworkImports(framework);
2824
- const isJunctionTable = table.pk.length > 1;
3017
+ const isJunctionTable2 = table.pk.length > 1;
2825
3018
  const hasForeignKeys = table.fks.length > 0;
2826
3019
  const foreignKeySetup = hasForeignKeys ? generateForeignKeySetup(table, model, clientPath) : null;
2827
3020
  const sampleData = generateSampleDataFromSchema(table, hasForeignKeys);
2828
3021
  const updateData = generateUpdateDataFromSchema(table);
2829
- if (isJunctionTable) {
3022
+ if (isJunctionTable2) {
2830
3023
  return `${imports}
2831
3024
  import { SDK } from '${clientPath}';
2832
3025
  import type { Insert${Type}, Update${Type}, Select${Type} } from '${clientPath}/types/${tableName}';
@@ -3630,11 +3823,11 @@ async function generate(configPath) {
3630
3823
  files.push({ path: join(clientDir, "base-client.ts"), content: emitBaseClient() });
3631
3824
  files.push({
3632
3825
  path: join(serverDir, "include-builder.ts"),
3633
- content: emitIncludeBuilder(graph, cfg.includeDepthLimit || 3)
3826
+ content: emitIncludeBuilder(graph, cfg.includeMethodsDepth || 2)
3634
3827
  });
3635
3828
  files.push({
3636
3829
  path: join(serverDir, "include-loader.ts"),
3637
- content: emitIncludeLoader(graph, model, cfg.includeDepthLimit || 3, cfg.useJsExtensions)
3830
+ content: emitIncludeLoader(graph, model, cfg.includeMethodsDepth || 2, cfg.useJsExtensions)
3638
3831
  });
3639
3832
  files.push({ path: join(serverDir, "logger.ts"), content: emitLogger() });
3640
3833
  if (normalizedAuth?.strategy && normalizedAuth.strategy !== "none") {
@@ -3660,7 +3853,7 @@ async function generate(configPath) {
3660
3853
  if (serverFramework === "hono") {
3661
3854
  routeContent = emitHonoRoutes(table, graph, {
3662
3855
  softDeleteColumn: cfg.softDeleteColumn || null,
3663
- includeDepthLimit: cfg.includeDepthLimit || 3,
3856
+ includeDepthLimit: cfg.includeMethodsDepth || 2,
3664
3857
  authStrategy: normalizedAuth?.strategy,
3665
3858
  useJsExtensions: cfg.useJsExtensions
3666
3859
  });
@@ -3673,7 +3866,11 @@ async function generate(configPath) {
3673
3866
  });
3674
3867
  files.push({
3675
3868
  path: join(clientDir, `${table.name}.ts`),
3676
- content: emitClient(table, cfg.useJsExtensionsClient)
3869
+ content: emitClient(table, graph, {
3870
+ useJsExtensions: cfg.useJsExtensionsClient,
3871
+ includeMethodsDepth: cfg.includeMethodsDepth ?? 2,
3872
+ skipJunctionTables: cfg.skipJunctionTables ?? true
3873
+ }, model)
3677
3874
  });
3678
3875
  }
3679
3876
  files.push({
package/dist/types.d.ts CHANGED
@@ -24,7 +24,9 @@ export interface Config {
24
24
  outServer?: string;
25
25
  outClient?: string;
26
26
  softDeleteColumn?: string | null;
27
- includeDepthLimit?: number;
27
+ dateType?: "date" | "string";
28
+ includeMethodsDepth?: number;
29
+ skipJunctionTables?: boolean;
28
30
  serverFramework?: "hono" | "express" | "fastify";
29
31
  auth?: AuthConfigInput;
30
32
  pull?: PullConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postgresdk",
3
- "version": "0.7.6",
3
+ "version": "0.9.0",
4
4
  "description": "Generate a typed server/client SDK from a Postgres schema (includes, Zod, Hono).",
5
5
  "type": "module",
6
6
  "bin": {