postgresdk 0.7.6 → 0.8.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 +205 -11
- package/dist/emit-client.d.ts +6 -1
- package/dist/emit-include-methods.d.ts +17 -0
- package/dist/index.js +205 -11
- package/dist/types.d.ts +2 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1914,18 +1914,211 @@ ${hasAuth ? `
|
|
|
1914
1914
|
|
|
1915
1915
|
// src/emit-client.ts
|
|
1916
1916
|
init_utils();
|
|
1917
|
-
|
|
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) {
|
|
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
|
+
const targetTable = Object.values(graph).find((t) => Object.values(t).some((e) => e.target === edge.target));
|
|
1997
|
+
if (opts.skipJunctionTables && edge.target.includes("_")) {
|
|
1998
|
+
continue;
|
|
1999
|
+
}
|
|
2000
|
+
const newPath = [...path, key];
|
|
2001
|
+
const newIsMany = [...isMany, edge.kind === "many"];
|
|
2002
|
+
const newTargets = [...targets, edge.target];
|
|
2003
|
+
const methodSuffix = pathToMethodSuffix(newPath);
|
|
2004
|
+
methods.push({
|
|
2005
|
+
name: `list${methodSuffix}`,
|
|
2006
|
+
path: newPath,
|
|
2007
|
+
isMany: newIsMany,
|
|
2008
|
+
targets: newTargets,
|
|
2009
|
+
returnType: `(${buildReturnType(baseTableName, newPath, newIsMany, newTargets, graph)})[]`,
|
|
2010
|
+
includeSpec: buildIncludeSpec(newPath)
|
|
2011
|
+
});
|
|
2012
|
+
methods.push({
|
|
2013
|
+
name: `getByPk${methodSuffix}`,
|
|
2014
|
+
path: newPath,
|
|
2015
|
+
isMany: newIsMany,
|
|
2016
|
+
targets: newTargets,
|
|
2017
|
+
returnType: `${buildReturnType(baseTableName, newPath, newIsMany, newTargets, graph)} | null`,
|
|
2018
|
+
includeSpec: buildIncludeSpec(newPath)
|
|
2019
|
+
});
|
|
2020
|
+
explore(edge.target, newPath, newIsMany, newTargets, new Set([...visited, edge.target]), depth + 1);
|
|
2021
|
+
}
|
|
2022
|
+
if (depth === 1 && Object.keys(currentEdges).length > 1 && Object.keys(currentEdges).length <= 3) {
|
|
2023
|
+
const edgeEntries = Object.entries(currentEdges);
|
|
2024
|
+
if (edgeEntries.length >= 2) {
|
|
2025
|
+
for (let i = 0;i < edgeEntries.length - 1; i++) {
|
|
2026
|
+
for (let j = i + 1;j < edgeEntries.length; j++) {
|
|
2027
|
+
const entry1 = edgeEntries[i];
|
|
2028
|
+
const entry2 = edgeEntries[j];
|
|
2029
|
+
if (!entry1 || !entry2)
|
|
2030
|
+
continue;
|
|
2031
|
+
const [key1, edge1] = entry1;
|
|
2032
|
+
const [key2, edge2] = entry2;
|
|
2033
|
+
if (opts.skipJunctionTables && (edge1.target.includes("_") || edge2.target.includes("_"))) {
|
|
2034
|
+
continue;
|
|
2035
|
+
}
|
|
2036
|
+
const combinedPath = [key1, key2];
|
|
2037
|
+
const combinedSuffix = `With${pascal(key1)}And${pascal(key2)}`;
|
|
2038
|
+
const type1 = `${key1}: ${edge1.kind === "many" ? `Select${pascal(edge1.target)}[]` : `Select${pascal(edge1.target)}`}`;
|
|
2039
|
+
const type2 = `${key2}: ${edge2.kind === "many" ? `Select${pascal(edge2.target)}[]` : `Select${pascal(edge2.target)}`}`;
|
|
2040
|
+
methods.push({
|
|
2041
|
+
name: `list${combinedSuffix}`,
|
|
2042
|
+
path: combinedPath,
|
|
2043
|
+
isMany: [edge1.kind === "many", edge2.kind === "many"],
|
|
2044
|
+
targets: [edge1.target, edge2.target],
|
|
2045
|
+
returnType: `(Select${pascal(baseTableName)} & { ${type1}; ${type2} })[]`,
|
|
2046
|
+
includeSpec: { [key1]: true, [key2]: true }
|
|
2047
|
+
});
|
|
2048
|
+
methods.push({
|
|
2049
|
+
name: `getByPk${combinedSuffix}`,
|
|
2050
|
+
path: combinedPath,
|
|
2051
|
+
isMany: [edge1.kind === "many", edge2.kind === "many"],
|
|
2052
|
+
targets: [edge1.target, edge2.target],
|
|
2053
|
+
returnType: `(Select${pascal(baseTableName)} & { ${type1}; ${type2} }) | null`,
|
|
2054
|
+
includeSpec: { [key1]: true, [key2]: true }
|
|
2055
|
+
});
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
explore(baseTableName, [], [], [], new Set([baseTableName]), 1);
|
|
2062
|
+
return methods;
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
// src/emit-client.ts
|
|
2066
|
+
function emitClient(table, graph, opts) {
|
|
1918
2067
|
const Type = pascal(table.name);
|
|
1919
|
-
const ext = useJsExtensions ? ".js" : "";
|
|
2068
|
+
const ext = opts.useJsExtensions ? ".js" : "";
|
|
1920
2069
|
const pkCols = Array.isArray(table.pk) ? table.pk : table.pk ? [table.pk] : [];
|
|
1921
2070
|
const safePk = pkCols.length ? pkCols : ["id"];
|
|
1922
2071
|
const hasCompositePk = safePk.length > 1;
|
|
1923
2072
|
const pkType = hasCompositePk ? `{ ${safePk.map((c) => `${c}: string`).join("; ")} }` : `string`;
|
|
1924
2073
|
const pkPathExpr = hasCompositePk ? safePk.map((c) => `pk.${c}`).join(` + "/" + `) : `pk`;
|
|
2074
|
+
const includeMethods = generateIncludeMethods(table, graph, {
|
|
2075
|
+
maxDepth: opts.includeMethodsDepth ?? 2,
|
|
2076
|
+
skipJunctionTables: opts.skipJunctionTables ?? true
|
|
2077
|
+
});
|
|
2078
|
+
const importedTypes = new Set;
|
|
2079
|
+
importedTypes.add(table.name);
|
|
2080
|
+
for (const method of includeMethods) {
|
|
2081
|
+
for (const target of method.targets) {
|
|
2082
|
+
importedTypes.add(target);
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
const typeImports = `import type { Insert${Type}, Update${Type}, Select${Type} } from "./types/${table.name}${ext}";`;
|
|
2086
|
+
const otherTableImports = [];
|
|
2087
|
+
for (const target of importedTypes) {
|
|
2088
|
+
if (target !== table.name) {
|
|
2089
|
+
otherTableImports.push(`import type { Select${pascal(target)} } from "./types/${target}${ext}";`);
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
let includeMethodsCode = "";
|
|
2093
|
+
for (const method of includeMethods) {
|
|
2094
|
+
const isGetByPk = method.name.startsWith("getByPk");
|
|
2095
|
+
const baseParams = isGetByPk ? "" : `params?: Omit<{ limit?: number; offset?: number; where?: any; orderBy?: string; order?: "asc" | "desc"; }, "include">`;
|
|
2096
|
+
if (isGetByPk) {
|
|
2097
|
+
const pkWhere = hasCompositePk ? `{ ${safePk.map((col) => `${col}: pk.${col}`).join(", ")} }` : `{ ${safePk[0] || "id"}: pk }`;
|
|
2098
|
+
const baseReturnType = method.returnType.replace(" | null", "");
|
|
2099
|
+
includeMethodsCode += `
|
|
2100
|
+
async ${method.name}(pk: ${pkType}): Promise<${method.returnType}> {
|
|
2101
|
+
const results = await this.post<${baseReturnType}[]>(\`\${this.resource}/list\`, {
|
|
2102
|
+
where: ${pkWhere},
|
|
2103
|
+
include: ${JSON.stringify(method.includeSpec)},
|
|
2104
|
+
limit: 1
|
|
2105
|
+
});
|
|
2106
|
+
return (results[0] as ${baseReturnType}) ?? null;
|
|
2107
|
+
}
|
|
2108
|
+
`;
|
|
2109
|
+
} else {
|
|
2110
|
+
includeMethodsCode += `
|
|
2111
|
+
async ${method.name}(${baseParams}): Promise<${method.returnType}> {
|
|
2112
|
+
return this.post<${method.returnType}>(\`\${this.resource}/list\`, { ...params, include: ${JSON.stringify(method.includeSpec)} });
|
|
2113
|
+
}
|
|
2114
|
+
`;
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
1925
2117
|
return `/* Generated. Do not edit. */
|
|
1926
2118
|
import { BaseClient } from "./base-client${ext}";
|
|
1927
|
-
|
|
1928
|
-
|
|
2119
|
+
${typeImports}
|
|
2120
|
+
${otherTableImports.join(`
|
|
2121
|
+
`)}
|
|
1929
2122
|
|
|
1930
2123
|
/**
|
|
1931
2124
|
* Client for ${table.name} table operations
|
|
@@ -1943,7 +2136,6 @@ export class ${Type}Client extends BaseClient {
|
|
|
1943
2136
|
}
|
|
1944
2137
|
|
|
1945
2138
|
async list(params?: {
|
|
1946
|
-
include?: ${Type}IncludeSpec;
|
|
1947
2139
|
limit?: number;
|
|
1948
2140
|
offset?: number;
|
|
1949
2141
|
where?: any;
|
|
@@ -1962,7 +2154,7 @@ export class ${Type}Client extends BaseClient {
|
|
|
1962
2154
|
const path = ${pkPathExpr};
|
|
1963
2155
|
return this.del<Select${Type} | null>(\`\${this.resource}/\${path}\`);
|
|
1964
2156
|
}
|
|
1965
|
-
}
|
|
2157
|
+
${includeMethodsCode}}
|
|
1966
2158
|
`;
|
|
1967
2159
|
}
|
|
1968
2160
|
function emitClientIndex(tables, useJsExtensions) {
|
|
@@ -2006,8 +2198,6 @@ export type { AuthConfig, HeaderMap, AuthHeadersProvider } from "./base-client${
|
|
|
2006
2198
|
|
|
2007
2199
|
`;
|
|
2008
2200
|
out += `export { BaseClient } from "./base-client${ext}";
|
|
2009
|
-
`;
|
|
2010
|
-
out += `export * from "./include-spec${ext}";
|
|
2011
2201
|
`;
|
|
2012
2202
|
out += `
|
|
2013
2203
|
// Zod schemas for form validation
|
|
@@ -3084,12 +3274,12 @@ function emitTableTest(table, model, clientPath, framework = "vitest") {
|
|
|
3084
3274
|
const Type = pascal(table.name);
|
|
3085
3275
|
const tableName = table.name;
|
|
3086
3276
|
const imports = getFrameworkImports(framework);
|
|
3087
|
-
const
|
|
3277
|
+
const isJunctionTable2 = table.pk.length > 1;
|
|
3088
3278
|
const hasForeignKeys = table.fks.length > 0;
|
|
3089
3279
|
const foreignKeySetup = hasForeignKeys ? generateForeignKeySetup(table, model, clientPath) : null;
|
|
3090
3280
|
const sampleData = generateSampleDataFromSchema(table, hasForeignKeys);
|
|
3091
3281
|
const updateData = generateUpdateDataFromSchema(table);
|
|
3092
|
-
if (
|
|
3282
|
+
if (isJunctionTable2) {
|
|
3093
3283
|
return `${imports}
|
|
3094
3284
|
import { SDK } from '${clientPath}';
|
|
3095
3285
|
import type { Insert${Type}, Update${Type}, Select${Type} } from '${clientPath}/types/${tableName}';
|
|
@@ -3936,7 +4126,11 @@ async function generate(configPath) {
|
|
|
3936
4126
|
});
|
|
3937
4127
|
files.push({
|
|
3938
4128
|
path: join(clientDir, `${table.name}.ts`),
|
|
3939
|
-
content: emitClient(table,
|
|
4129
|
+
content: emitClient(table, graph, {
|
|
4130
|
+
useJsExtensions: cfg.useJsExtensionsClient,
|
|
4131
|
+
includeMethodsDepth: cfg.includeMethodsDepth ?? 2,
|
|
4132
|
+
skipJunctionTables: cfg.skipJunctionTables ?? true
|
|
4133
|
+
})
|
|
3940
4134
|
});
|
|
3941
4135
|
}
|
|
3942
4136
|
files.push({
|
package/dist/emit-client.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import type { Table } from "./introspect";
|
|
2
|
-
|
|
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
|
+
}): 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
|
+
}): IncludeMethod[];
|
package/dist/index.js
CHANGED
|
@@ -1651,18 +1651,211 @@ ${hasAuth ? `
|
|
|
1651
1651
|
|
|
1652
1652
|
// src/emit-client.ts
|
|
1653
1653
|
init_utils();
|
|
1654
|
-
|
|
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) {
|
|
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
|
+
const targetTable = Object.values(graph).find((t) => Object.values(t).some((e) => e.target === edge.target));
|
|
1734
|
+
if (opts.skipJunctionTables && edge.target.includes("_")) {
|
|
1735
|
+
continue;
|
|
1736
|
+
}
|
|
1737
|
+
const newPath = [...path, key];
|
|
1738
|
+
const newIsMany = [...isMany, edge.kind === "many"];
|
|
1739
|
+
const newTargets = [...targets, edge.target];
|
|
1740
|
+
const methodSuffix = pathToMethodSuffix(newPath);
|
|
1741
|
+
methods.push({
|
|
1742
|
+
name: `list${methodSuffix}`,
|
|
1743
|
+
path: newPath,
|
|
1744
|
+
isMany: newIsMany,
|
|
1745
|
+
targets: newTargets,
|
|
1746
|
+
returnType: `(${buildReturnType(baseTableName, newPath, newIsMany, newTargets, graph)})[]`,
|
|
1747
|
+
includeSpec: buildIncludeSpec(newPath)
|
|
1748
|
+
});
|
|
1749
|
+
methods.push({
|
|
1750
|
+
name: `getByPk${methodSuffix}`,
|
|
1751
|
+
path: newPath,
|
|
1752
|
+
isMany: newIsMany,
|
|
1753
|
+
targets: newTargets,
|
|
1754
|
+
returnType: `${buildReturnType(baseTableName, newPath, newIsMany, newTargets, graph)} | null`,
|
|
1755
|
+
includeSpec: buildIncludeSpec(newPath)
|
|
1756
|
+
});
|
|
1757
|
+
explore(edge.target, newPath, newIsMany, newTargets, new Set([...visited, edge.target]), depth + 1);
|
|
1758
|
+
}
|
|
1759
|
+
if (depth === 1 && Object.keys(currentEdges).length > 1 && Object.keys(currentEdges).length <= 3) {
|
|
1760
|
+
const edgeEntries = Object.entries(currentEdges);
|
|
1761
|
+
if (edgeEntries.length >= 2) {
|
|
1762
|
+
for (let i = 0;i < edgeEntries.length - 1; i++) {
|
|
1763
|
+
for (let j = i + 1;j < edgeEntries.length; j++) {
|
|
1764
|
+
const entry1 = edgeEntries[i];
|
|
1765
|
+
const entry2 = edgeEntries[j];
|
|
1766
|
+
if (!entry1 || !entry2)
|
|
1767
|
+
continue;
|
|
1768
|
+
const [key1, edge1] = entry1;
|
|
1769
|
+
const [key2, edge2] = entry2;
|
|
1770
|
+
if (opts.skipJunctionTables && (edge1.target.includes("_") || edge2.target.includes("_"))) {
|
|
1771
|
+
continue;
|
|
1772
|
+
}
|
|
1773
|
+
const combinedPath = [key1, key2];
|
|
1774
|
+
const combinedSuffix = `With${pascal(key1)}And${pascal(key2)}`;
|
|
1775
|
+
const type1 = `${key1}: ${edge1.kind === "many" ? `Select${pascal(edge1.target)}[]` : `Select${pascal(edge1.target)}`}`;
|
|
1776
|
+
const type2 = `${key2}: ${edge2.kind === "many" ? `Select${pascal(edge2.target)}[]` : `Select${pascal(edge2.target)}`}`;
|
|
1777
|
+
methods.push({
|
|
1778
|
+
name: `list${combinedSuffix}`,
|
|
1779
|
+
path: combinedPath,
|
|
1780
|
+
isMany: [edge1.kind === "many", edge2.kind === "many"],
|
|
1781
|
+
targets: [edge1.target, edge2.target],
|
|
1782
|
+
returnType: `(Select${pascal(baseTableName)} & { ${type1}; ${type2} })[]`,
|
|
1783
|
+
includeSpec: { [key1]: true, [key2]: true }
|
|
1784
|
+
});
|
|
1785
|
+
methods.push({
|
|
1786
|
+
name: `getByPk${combinedSuffix}`,
|
|
1787
|
+
path: combinedPath,
|
|
1788
|
+
isMany: [edge1.kind === "many", edge2.kind === "many"],
|
|
1789
|
+
targets: [edge1.target, edge2.target],
|
|
1790
|
+
returnType: `(Select${pascal(baseTableName)} & { ${type1}; ${type2} }) | null`,
|
|
1791
|
+
includeSpec: { [key1]: true, [key2]: true }
|
|
1792
|
+
});
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
explore(baseTableName, [], [], [], new Set([baseTableName]), 1);
|
|
1799
|
+
return methods;
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
// src/emit-client.ts
|
|
1803
|
+
function emitClient(table, graph, opts) {
|
|
1655
1804
|
const Type = pascal(table.name);
|
|
1656
|
-
const ext = useJsExtensions ? ".js" : "";
|
|
1805
|
+
const ext = opts.useJsExtensions ? ".js" : "";
|
|
1657
1806
|
const pkCols = Array.isArray(table.pk) ? table.pk : table.pk ? [table.pk] : [];
|
|
1658
1807
|
const safePk = pkCols.length ? pkCols : ["id"];
|
|
1659
1808
|
const hasCompositePk = safePk.length > 1;
|
|
1660
1809
|
const pkType = hasCompositePk ? `{ ${safePk.map((c) => `${c}: string`).join("; ")} }` : `string`;
|
|
1661
1810
|
const pkPathExpr = hasCompositePk ? safePk.map((c) => `pk.${c}`).join(` + "/" + `) : `pk`;
|
|
1811
|
+
const includeMethods = generateIncludeMethods(table, graph, {
|
|
1812
|
+
maxDepth: opts.includeMethodsDepth ?? 2,
|
|
1813
|
+
skipJunctionTables: opts.skipJunctionTables ?? true
|
|
1814
|
+
});
|
|
1815
|
+
const importedTypes = new Set;
|
|
1816
|
+
importedTypes.add(table.name);
|
|
1817
|
+
for (const method of includeMethods) {
|
|
1818
|
+
for (const target of method.targets) {
|
|
1819
|
+
importedTypes.add(target);
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
const typeImports = `import type { Insert${Type}, Update${Type}, Select${Type} } from "./types/${table.name}${ext}";`;
|
|
1823
|
+
const otherTableImports = [];
|
|
1824
|
+
for (const target of importedTypes) {
|
|
1825
|
+
if (target !== table.name) {
|
|
1826
|
+
otherTableImports.push(`import type { Select${pascal(target)} } from "./types/${target}${ext}";`);
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
let includeMethodsCode = "";
|
|
1830
|
+
for (const method of includeMethods) {
|
|
1831
|
+
const isGetByPk = method.name.startsWith("getByPk");
|
|
1832
|
+
const baseParams = isGetByPk ? "" : `params?: Omit<{ limit?: number; offset?: number; where?: any; orderBy?: string; order?: "asc" | "desc"; }, "include">`;
|
|
1833
|
+
if (isGetByPk) {
|
|
1834
|
+
const pkWhere = hasCompositePk ? `{ ${safePk.map((col) => `${col}: pk.${col}`).join(", ")} }` : `{ ${safePk[0] || "id"}: pk }`;
|
|
1835
|
+
const baseReturnType = method.returnType.replace(" | null", "");
|
|
1836
|
+
includeMethodsCode += `
|
|
1837
|
+
async ${method.name}(pk: ${pkType}): Promise<${method.returnType}> {
|
|
1838
|
+
const results = await this.post<${baseReturnType}[]>(\`\${this.resource}/list\`, {
|
|
1839
|
+
where: ${pkWhere},
|
|
1840
|
+
include: ${JSON.stringify(method.includeSpec)},
|
|
1841
|
+
limit: 1
|
|
1842
|
+
});
|
|
1843
|
+
return (results[0] as ${baseReturnType}) ?? null;
|
|
1844
|
+
}
|
|
1845
|
+
`;
|
|
1846
|
+
} else {
|
|
1847
|
+
includeMethodsCode += `
|
|
1848
|
+
async ${method.name}(${baseParams}): Promise<${method.returnType}> {
|
|
1849
|
+
return this.post<${method.returnType}>(\`\${this.resource}/list\`, { ...params, include: ${JSON.stringify(method.includeSpec)} });
|
|
1850
|
+
}
|
|
1851
|
+
`;
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1662
1854
|
return `/* Generated. Do not edit. */
|
|
1663
1855
|
import { BaseClient } from "./base-client${ext}";
|
|
1664
|
-
|
|
1665
|
-
|
|
1856
|
+
${typeImports}
|
|
1857
|
+
${otherTableImports.join(`
|
|
1858
|
+
`)}
|
|
1666
1859
|
|
|
1667
1860
|
/**
|
|
1668
1861
|
* Client for ${table.name} table operations
|
|
@@ -1680,7 +1873,6 @@ export class ${Type}Client extends BaseClient {
|
|
|
1680
1873
|
}
|
|
1681
1874
|
|
|
1682
1875
|
async list(params?: {
|
|
1683
|
-
include?: ${Type}IncludeSpec;
|
|
1684
1876
|
limit?: number;
|
|
1685
1877
|
offset?: number;
|
|
1686
1878
|
where?: any;
|
|
@@ -1699,7 +1891,7 @@ export class ${Type}Client extends BaseClient {
|
|
|
1699
1891
|
const path = ${pkPathExpr};
|
|
1700
1892
|
return this.del<Select${Type} | null>(\`\${this.resource}/\${path}\`);
|
|
1701
1893
|
}
|
|
1702
|
-
}
|
|
1894
|
+
${includeMethodsCode}}
|
|
1703
1895
|
`;
|
|
1704
1896
|
}
|
|
1705
1897
|
function emitClientIndex(tables, useJsExtensions) {
|
|
@@ -1743,8 +1935,6 @@ export type { AuthConfig, HeaderMap, AuthHeadersProvider } from "./base-client${
|
|
|
1743
1935
|
|
|
1744
1936
|
`;
|
|
1745
1937
|
out += `export { BaseClient } from "./base-client${ext}";
|
|
1746
|
-
`;
|
|
1747
|
-
out += `export * from "./include-spec${ext}";
|
|
1748
1938
|
`;
|
|
1749
1939
|
out += `
|
|
1750
1940
|
// Zod schemas for form validation
|
|
@@ -2821,12 +3011,12 @@ function emitTableTest(table, model, clientPath, framework = "vitest") {
|
|
|
2821
3011
|
const Type = pascal(table.name);
|
|
2822
3012
|
const tableName = table.name;
|
|
2823
3013
|
const imports = getFrameworkImports(framework);
|
|
2824
|
-
const
|
|
3014
|
+
const isJunctionTable2 = table.pk.length > 1;
|
|
2825
3015
|
const hasForeignKeys = table.fks.length > 0;
|
|
2826
3016
|
const foreignKeySetup = hasForeignKeys ? generateForeignKeySetup(table, model, clientPath) : null;
|
|
2827
3017
|
const sampleData = generateSampleDataFromSchema(table, hasForeignKeys);
|
|
2828
3018
|
const updateData = generateUpdateDataFromSchema(table);
|
|
2829
|
-
if (
|
|
3019
|
+
if (isJunctionTable2) {
|
|
2830
3020
|
return `${imports}
|
|
2831
3021
|
import { SDK } from '${clientPath}';
|
|
2832
3022
|
import type { Insert${Type}, Update${Type}, Select${Type} } from '${clientPath}/types/${tableName}';
|
|
@@ -3673,7 +3863,11 @@ async function generate(configPath) {
|
|
|
3673
3863
|
});
|
|
3674
3864
|
files.push({
|
|
3675
3865
|
path: join(clientDir, `${table.name}.ts`),
|
|
3676
|
-
content: emitClient(table,
|
|
3866
|
+
content: emitClient(table, graph, {
|
|
3867
|
+
useJsExtensions: cfg.useJsExtensionsClient,
|
|
3868
|
+
includeMethodsDepth: cfg.includeMethodsDepth ?? 2,
|
|
3869
|
+
skipJunctionTables: cfg.skipJunctionTables ?? true
|
|
3870
|
+
})
|
|
3677
3871
|
});
|
|
3678
3872
|
}
|
|
3679
3873
|
files.push({
|
package/dist/types.d.ts
CHANGED
|
@@ -25,6 +25,8 @@ export interface Config {
|
|
|
25
25
|
outClient?: string;
|
|
26
26
|
softDeleteColumn?: string | null;
|
|
27
27
|
includeDepthLimit?: number;
|
|
28
|
+
includeMethodsDepth?: number;
|
|
29
|
+
skipJunctionTables?: boolean;
|
|
28
30
|
serverFramework?: "hono" | "express" | "fastify";
|
|
29
31
|
auth?: AuthConfigInput;
|
|
30
32
|
pull?: PullConfig;
|