postgresdk 0.8.0 → 0.9.1

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
@@ -486,6 +486,157 @@ async function ensureDirs(dirs) {
486
486
  var pascal = (s) => s.split(/[_\s-]+/).map((w) => w?.[0] ? w[0].toUpperCase() + w.slice(1) : "").join("");
487
487
  var init_utils = () => {};
488
488
 
489
+ // src/emit-include-methods.ts
490
+ function isJunctionTable(table) {
491
+ if (!table.name.includes("_"))
492
+ return false;
493
+ const fkColumns = new Set(table.fks.flatMap((fk) => fk.from));
494
+ const nonPkColumns = table.columns.filter((c) => !table.pk.includes(c.name));
495
+ return nonPkColumns.every((c) => fkColumns.has(c.name));
496
+ }
497
+ function pathToMethodSuffix(path) {
498
+ return "With" + path.map((p) => pascal(p)).join("And");
499
+ }
500
+ function buildReturnType(baseTable, path, isMany, targets, graph) {
501
+ const BaseType = `Select${pascal(baseTable)}`;
502
+ if (path.length === 0)
503
+ return BaseType;
504
+ let type = BaseType;
505
+ let currentTable = baseTable;
506
+ const parts = [];
507
+ for (let i = 0;i < path.length; i++) {
508
+ const key = path[i];
509
+ const target = targets[i];
510
+ if (!key || !target)
511
+ continue;
512
+ const targetType = `Select${pascal(target)}`;
513
+ if (i === 0) {
514
+ parts.push(`${key}: ${isMany[i] ? `${targetType}[]` : targetType}`);
515
+ } else {
516
+ let nestedType = targetType;
517
+ for (let j = i;j < path.length; j++) {
518
+ if (j > i) {
519
+ const nestedKey = path[j];
520
+ const nestedTarget = targets[j];
521
+ if (!nestedKey || !nestedTarget)
522
+ continue;
523
+ const nestedTargetType = `Select${pascal(nestedTarget)}`;
524
+ nestedType = `${nestedType} & { ${nestedKey}: ${isMany[j] ? `${nestedTargetType}[]` : nestedTargetType} }`;
525
+ }
526
+ }
527
+ const prevKey = path[i - 1];
528
+ const prevTarget = targets[i - 1];
529
+ if (prevKey && prevTarget) {
530
+ parts[parts.length - 1] = `${prevKey}: ${isMany[i - 1] ? `(Select${pascal(prevTarget)} & { ${key}: ${isMany[i] ? `${targetType}[]` : targetType} })[]` : `Select${pascal(prevTarget)} & { ${key}: ${isMany[i] ? `${targetType}[]` : targetType} }`}`;
531
+ }
532
+ break;
533
+ }
534
+ }
535
+ return `${type} & { ${parts.join("; ")} }`;
536
+ }
537
+ function buildIncludeSpec(path) {
538
+ if (path.length === 0)
539
+ return {};
540
+ if (path.length === 1)
541
+ return { [path[0]]: true };
542
+ let spec = true;
543
+ for (let i = path.length - 1;i > 0; i--) {
544
+ const key = path[i];
545
+ if (!key)
546
+ continue;
547
+ spec = { [key]: spec };
548
+ }
549
+ const rootKey = path[0];
550
+ return rootKey ? { [rootKey]: spec } : {};
551
+ }
552
+ function generateIncludeMethods(table, graph, opts, allTables) {
553
+ const methods = [];
554
+ const baseTableName = table.name;
555
+ if (opts.skipJunctionTables && isJunctionTable(table)) {
556
+ return methods;
557
+ }
558
+ const edges = graph[baseTableName] || {};
559
+ function explore(currentTable, path, isMany, targets, visited, depth) {
560
+ if (depth > opts.maxDepth)
561
+ return;
562
+ const currentEdges = graph[currentTable] || {};
563
+ for (const [key, edge] of Object.entries(currentEdges)) {
564
+ if (visited.has(edge.target))
565
+ continue;
566
+ if (opts.skipJunctionTables && allTables) {
567
+ const targetTable = allTables.find((t) => t.name === edge.target);
568
+ if (targetTable && isJunctionTable(targetTable)) {
569
+ continue;
570
+ }
571
+ }
572
+ const newPath = [...path, key];
573
+ const newIsMany = [...isMany, edge.kind === "many"];
574
+ const newTargets = [...targets, edge.target];
575
+ const methodSuffix = pathToMethodSuffix(newPath);
576
+ methods.push({
577
+ name: `list${methodSuffix}`,
578
+ path: newPath,
579
+ isMany: newIsMany,
580
+ targets: newTargets,
581
+ returnType: `(${buildReturnType(baseTableName, newPath, newIsMany, newTargets, graph)})[]`,
582
+ includeSpec: buildIncludeSpec(newPath)
583
+ });
584
+ methods.push({
585
+ name: `getByPk${methodSuffix}`,
586
+ path: newPath,
587
+ isMany: newIsMany,
588
+ targets: newTargets,
589
+ returnType: `${buildReturnType(baseTableName, newPath, newIsMany, newTargets, graph)} | null`,
590
+ includeSpec: buildIncludeSpec(newPath)
591
+ });
592
+ explore(edge.target, newPath, newIsMany, newTargets, new Set([...visited, edge.target]), depth + 1);
593
+ }
594
+ if (depth === 1 && Object.keys(currentEdges).length > 1 && Object.keys(currentEdges).length <= 3) {
595
+ const edgeEntries = Object.entries(currentEdges);
596
+ if (edgeEntries.length >= 2) {
597
+ for (let i = 0;i < edgeEntries.length - 1; i++) {
598
+ for (let j = i + 1;j < edgeEntries.length; j++) {
599
+ const entry1 = edgeEntries[i];
600
+ const entry2 = edgeEntries[j];
601
+ if (!entry1 || !entry2)
602
+ continue;
603
+ const [key1, edge1] = entry1;
604
+ const [key2, edge2] = entry2;
605
+ if (opts.skipJunctionTables && (edge1.target.includes("_") || edge2.target.includes("_"))) {
606
+ continue;
607
+ }
608
+ const combinedPath = [key1, key2];
609
+ const combinedSuffix = `With${pascal(key1)}And${pascal(key2)}`;
610
+ const type1 = `${key1}: ${edge1.kind === "many" ? `Select${pascal(edge1.target)}[]` : `Select${pascal(edge1.target)}`}`;
611
+ const type2 = `${key2}: ${edge2.kind === "many" ? `Select${pascal(edge2.target)}[]` : `Select${pascal(edge2.target)}`}`;
612
+ methods.push({
613
+ name: `list${combinedSuffix}`,
614
+ path: combinedPath,
615
+ isMany: [edge1.kind === "many", edge2.kind === "many"],
616
+ targets: [edge1.target, edge2.target],
617
+ returnType: `(Select${pascal(baseTableName)} & { ${type1}; ${type2} })[]`,
618
+ includeSpec: { [key1]: true, [key2]: true }
619
+ });
620
+ methods.push({
621
+ name: `getByPk${combinedSuffix}`,
622
+ path: combinedPath,
623
+ isMany: [edge1.kind === "many", edge2.kind === "many"],
624
+ targets: [edge1.target, edge2.target],
625
+ returnType: `(Select${pascal(baseTableName)} & { ${type1}; ${type2} }) | null`,
626
+ includeSpec: { [key1]: true, [key2]: true }
627
+ });
628
+ }
629
+ }
630
+ }
631
+ }
632
+ }
633
+ explore(baseTableName, [], [], [], new Set([baseTableName]), 1);
634
+ return methods;
635
+ }
636
+ var init_emit_include_methods = __esm(() => {
637
+ init_utils();
638
+ });
639
+
489
640
  // src/emit-sdk-contract.ts
490
641
  var exports_emit_sdk_contract = {};
491
642
  __export(exports_emit_sdk_contract, {
@@ -493,7 +644,7 @@ __export(exports_emit_sdk_contract, {
493
644
  generateUnifiedContract: () => generateUnifiedContract,
494
645
  emitUnifiedContract: () => emitUnifiedContract
495
646
  });
496
- function generateUnifiedContract(model, config) {
647
+ function generateUnifiedContract(model, config, graph) {
497
648
  const resources = [];
498
649
  const relationships = [];
499
650
  const tables = model && model.tables ? Object.values(model.tables) : [];
@@ -501,7 +652,7 @@ function generateUnifiedContract(model, config) {
501
652
  console.log(`[SDK Contract] Processing ${tables.length} tables`);
502
653
  }
503
654
  for (const table of tables) {
504
- resources.push(generateResourceWithSDK(table, model));
655
+ resources.push(generateResourceWithSDK(table, model, graph, config));
505
656
  for (const fk of table.fks) {
506
657
  relationships.push({
507
658
  from: table.name,
@@ -619,7 +770,7 @@ function generateSDKAuthExamples(auth) {
619
770
  });
620
771
  return examples;
621
772
  }
622
- function generateResourceWithSDK(table, model) {
773
+ function generateResourceWithSDK(table, model, graph, config) {
623
774
  const Type = pascal(table.name);
624
775
  const tableName = table.name;
625
776
  const basePath = `/v1/${tableName}`;
@@ -641,11 +792,6 @@ const filtered = await sdk.${tableName}.list({
641
792
  ${table.columns[0]?.name || "field"}_like: 'search',
642
793
  order_by: '${table.columns[0]?.name || "created_at"}',
643
794
  order_dir: 'desc'
644
- });
645
-
646
- // With related data
647
- const withRelations = await sdk.${tableName}.list({
648
- include: '${table.fks[0]?.toTable || "related_table"}'
649
795
  });`,
650
796
  correspondsTo: `GET ${basePath}`
651
797
  });
@@ -659,16 +805,11 @@ const withRelations = await sdk.${tableName}.list({
659
805
  if (hasSinglePK) {
660
806
  sdkMethods.push({
661
807
  name: "getByPk",
662
- signature: `getByPk(${pkField}: string, params?: GetParams): Promise<${Type} | null>`,
808
+ signature: `getByPk(${pkField}: string): Promise<${Type} | null>`,
663
809
  description: `Get a single ${tableName} by primary key`,
664
810
  example: `// Get by ID
665
811
  const item = await sdk.${tableName}.getByPk('123e4567-e89b-12d3-a456-426614174000');
666
812
 
667
- // With related data
668
- const withRelations = await sdk.${tableName}.getByPk('123', {
669
- include: '${table.fks[0]?.toTable || "related_table"}'
670
- });
671
-
672
813
  // Check if exists
673
814
  if (item === null) {
674
815
  console.log('Not found');
@@ -679,9 +820,6 @@ if (item === null) {
679
820
  method: "GET",
680
821
  path: `${basePath}/:${pkField}`,
681
822
  description: `Get ${tableName} by ID`,
682
- queryParameters: {
683
- include: "string - Comma-separated list of related resources"
684
- },
685
823
  responseBody: `${Type}`
686
824
  });
687
825
  }
@@ -744,6 +882,31 @@ console.log('Deleted:', deleted);`,
744
882
  responseBody: `${Type}`
745
883
  });
746
884
  }
885
+ if (graph && config) {
886
+ const allTables = model && model.tables ? Object.values(model.tables) : undefined;
887
+ const includeMethods = generateIncludeMethods(table, graph, {
888
+ maxDepth: config.includeMethodsDepth ?? 2,
889
+ skipJunctionTables: config.skipJunctionTables ?? true
890
+ }, allTables);
891
+ for (const method of includeMethods) {
892
+ const isGetByPk = method.name.startsWith("getByPk");
893
+ const exampleCall = isGetByPk ? `const result = await sdk.${tableName}.${method.name}('123e4567-e89b-12d3-a456-426614174000');` : `const results = await sdk.${tableName}.${method.name}();
894
+
895
+ // With filters and pagination
896
+ const filtered = await sdk.${tableName}.${method.name}({
897
+ limit: 20,
898
+ offset: 0,
899
+ where: { /* filter conditions */ }
900
+ });`;
901
+ sdkMethods.push({
902
+ name: method.name,
903
+ signature: `${method.name}(${isGetByPk ? `${pkField}: string` : "params?: ListParams"}): ${method.returnType}`,
904
+ description: `Get ${tableName} with included ${method.path.join(", ")} data`,
905
+ example: exampleCall,
906
+ correspondsTo: `POST ${basePath}/list`
907
+ });
908
+ }
909
+ }
747
910
  const fields = table.columns.map((col) => generateFieldContract(col, table));
748
911
  return {
749
912
  name: Type,
@@ -892,8 +1055,7 @@ function generateQueryParams(table) {
892
1055
  limit: "number - Max records to return (default: 50)",
893
1056
  offset: "number - Records to skip",
894
1057
  order_by: "string - Field to sort by",
895
- order_dir: "'asc' | 'desc' - Sort direction",
896
- include: "string - Related resources to include"
1058
+ order_dir: "'asc' | 'desc' - Sort direction"
897
1059
  };
898
1060
  let filterCount = 0;
899
1061
  for (const col of table.columns) {
@@ -1080,8 +1242,8 @@ function generateUnifiedContractMarkdown(contract) {
1080
1242
  return lines.join(`
1081
1243
  `);
1082
1244
  }
1083
- function emitUnifiedContract(model, config) {
1084
- const contract = generateUnifiedContract(model, config);
1245
+ function emitUnifiedContract(model, config, graph) {
1246
+ const contract = generateUnifiedContract(model, config, graph);
1085
1247
  const contractJson = JSON.stringify(contract, null, 2);
1086
1248
  return `/**
1087
1249
  * Unified API & SDK Contract
@@ -1137,6 +1299,7 @@ import type * as Types from './client/types';
1137
1299
  }
1138
1300
  var init_emit_sdk_contract = __esm(() => {
1139
1301
  init_utils();
1302
+ init_emit_include_methods();
1140
1303
  });
1141
1304
 
1142
1305
  // src/cli-init.ts
@@ -1914,156 +2077,8 @@ ${hasAuth ? `
1914
2077
 
1915
2078
  // src/emit-client.ts
1916
2079
  init_utils();
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) {
2080
+ init_emit_include_methods();
2081
+ function emitClient(table, graph, opts, model) {
2067
2082
  const Type = pascal(table.name);
2068
2083
  const ext = opts.useJsExtensions ? ".js" : "";
2069
2084
  const pkCols = Array.isArray(table.pk) ? table.pk : table.pk ? [table.pk] : [];
@@ -2071,10 +2086,11 @@ function emitClient(table, graph, opts) {
2071
2086
  const hasCompositePk = safePk.length > 1;
2072
2087
  const pkType = hasCompositePk ? `{ ${safePk.map((c) => `${c}: string`).join("; ")} }` : `string`;
2073
2088
  const pkPathExpr = hasCompositePk ? safePk.map((c) => `pk.${c}`).join(` + "/" + `) : `pk`;
2089
+ const allTables = model ? Object.values(model.tables) : undefined;
2074
2090
  const includeMethods = generateIncludeMethods(table, graph, {
2075
2091
  maxDepth: opts.includeMethodsDepth ?? 2,
2076
2092
  skipJunctionTables: opts.skipJunctionTables ?? true
2077
- });
2093
+ }, allTables);
2078
2094
  const importedTypes = new Set;
2079
2095
  importedTypes.add(table.name);
2080
2096
  for (const method of includeMethods) {
@@ -2084,7 +2100,7 @@ function emitClient(table, graph, opts) {
2084
2100
  }
2085
2101
  const typeImports = `import type { Insert${Type}, Update${Type}, Select${Type} } from "./types/${table.name}${ext}";`;
2086
2102
  const otherTableImports = [];
2087
- for (const target of importedTypes) {
2103
+ for (const target of Array.from(importedTypes)) {
2088
2104
  if (target !== table.name) {
2089
2105
  otherTableImports.push(`import type { Select${pascal(target)} } from "./types/${target}${ext}";`);
2090
2106
  }
@@ -4083,11 +4099,11 @@ async function generate(configPath) {
4083
4099
  files.push({ path: join(clientDir, "base-client.ts"), content: emitBaseClient() });
4084
4100
  files.push({
4085
4101
  path: join(serverDir, "include-builder.ts"),
4086
- content: emitIncludeBuilder(graph, cfg.includeDepthLimit || 3)
4102
+ content: emitIncludeBuilder(graph, cfg.includeMethodsDepth || 2)
4087
4103
  });
4088
4104
  files.push({
4089
4105
  path: join(serverDir, "include-loader.ts"),
4090
- content: emitIncludeLoader(graph, model, cfg.includeDepthLimit || 3, cfg.useJsExtensions)
4106
+ content: emitIncludeLoader(graph, model, cfg.includeMethodsDepth || 2, cfg.useJsExtensions)
4091
4107
  });
4092
4108
  files.push({ path: join(serverDir, "logger.ts"), content: emitLogger() });
4093
4109
  if (normalizedAuth?.strategy && normalizedAuth.strategy !== "none") {
@@ -4113,7 +4129,7 @@ async function generate(configPath) {
4113
4129
  if (serverFramework === "hono") {
4114
4130
  routeContent = emitHonoRoutes(table, graph, {
4115
4131
  softDeleteColumn: cfg.softDeleteColumn || null,
4116
- includeDepthLimit: cfg.includeDepthLimit || 3,
4132
+ includeDepthLimit: cfg.includeMethodsDepth || 2,
4117
4133
  authStrategy: normalizedAuth?.strategy,
4118
4134
  useJsExtensions: cfg.useJsExtensions
4119
4135
  });
@@ -4130,7 +4146,7 @@ async function generate(configPath) {
4130
4146
  useJsExtensions: cfg.useJsExtensionsClient,
4131
4147
  includeMethodsDepth: cfg.includeMethodsDepth ?? 2,
4132
4148
  skipJunctionTables: cfg.skipJunctionTables ?? true
4133
- })
4149
+ }, model)
4134
4150
  });
4135
4151
  }
4136
4152
  files.push({
@@ -4159,7 +4175,7 @@ async function generate(configPath) {
4159
4175
  if (process.env.SDK_DEBUG) {
4160
4176
  console.log(`[Index] Model has ${Object.keys(model.tables || {}).length} tables before contract generation`);
4161
4177
  }
4162
- const contract = generateUnifiedContract2(model, cfg);
4178
+ const contract = generateUnifiedContract2(model, cfg, graph);
4163
4179
  files.push({
4164
4180
  path: join(serverDir, "CONTRACT.md"),
4165
4181
  content: generateUnifiedContractMarkdown2(contract)
@@ -1,8 +1,8 @@
1
- import type { Table } from "./introspect";
1
+ import type { Table, Model } from "./introspect";
2
2
  import type { Graph } from "./rel-classify";
3
3
  export declare function emitClient(table: Table, graph: Graph, opts: {
4
4
  useJsExtensions?: boolean;
5
5
  includeMethodsDepth?: number;
6
6
  skipJunctionTables?: boolean;
7
- }): string;
7
+ }, model?: Model): string;
8
8
  export declare function emitClientIndex(tables: Table[], useJsExtensions?: boolean): string;
@@ -14,4 +14,4 @@ export type IncludeMethod = {
14
14
  export declare function generateIncludeMethods(table: Table, graph: Graph, opts: {
15
15
  maxDepth: number;
16
16
  skipJunctionTables: boolean;
17
- }): IncludeMethod[];
17
+ }, allTables?: Table[]): IncludeMethod[];
@@ -1,5 +1,6 @@
1
1
  import type { Model } from "./introspect";
2
2
  import type { Config, AuthConfig } from "./types";
3
+ import type { Graph } from "./rel-classify";
3
4
  export interface UnifiedContract {
4
5
  version: string;
5
6
  generatedAt: string;
@@ -70,7 +71,7 @@ export interface RelationshipContract {
70
71
  */
71
72
  export declare function generateUnifiedContract(model: Model, config: Config & {
72
73
  auth?: AuthConfig;
73
- }): UnifiedContract;
74
+ }, graph?: Graph): UnifiedContract;
74
75
  /**
75
76
  * Generate markdown documentation for the unified contract
76
77
  */
@@ -80,4 +81,4 @@ export declare function generateUnifiedContractMarkdown(contract: UnifiedContrac
80
81
  */
81
82
  export declare function emitUnifiedContract(model: Model, config: Config & {
82
83
  auth?: AuthConfig;
83
- }): string;
84
+ }, graph?: Graph): string;
package/dist/index.js CHANGED
@@ -485,6 +485,157 @@ async function ensureDirs(dirs) {
485
485
  var pascal = (s) => s.split(/[_\s-]+/).map((w) => w?.[0] ? w[0].toUpperCase() + w.slice(1) : "").join("");
486
486
  var init_utils = () => {};
487
487
 
488
+ // src/emit-include-methods.ts
489
+ function isJunctionTable(table) {
490
+ if (!table.name.includes("_"))
491
+ return false;
492
+ const fkColumns = new Set(table.fks.flatMap((fk) => fk.from));
493
+ const nonPkColumns = table.columns.filter((c) => !table.pk.includes(c.name));
494
+ return nonPkColumns.every((c) => fkColumns.has(c.name));
495
+ }
496
+ function pathToMethodSuffix(path) {
497
+ return "With" + path.map((p) => pascal(p)).join("And");
498
+ }
499
+ function buildReturnType(baseTable, path, isMany, targets, graph) {
500
+ const BaseType = `Select${pascal(baseTable)}`;
501
+ if (path.length === 0)
502
+ return BaseType;
503
+ let type = BaseType;
504
+ let currentTable = baseTable;
505
+ const parts = [];
506
+ for (let i = 0;i < path.length; i++) {
507
+ const key = path[i];
508
+ const target = targets[i];
509
+ if (!key || !target)
510
+ continue;
511
+ const targetType = `Select${pascal(target)}`;
512
+ if (i === 0) {
513
+ parts.push(`${key}: ${isMany[i] ? `${targetType}[]` : targetType}`);
514
+ } else {
515
+ let nestedType = targetType;
516
+ for (let j = i;j < path.length; j++) {
517
+ if (j > i) {
518
+ const nestedKey = path[j];
519
+ const nestedTarget = targets[j];
520
+ if (!nestedKey || !nestedTarget)
521
+ continue;
522
+ const nestedTargetType = `Select${pascal(nestedTarget)}`;
523
+ nestedType = `${nestedType} & { ${nestedKey}: ${isMany[j] ? `${nestedTargetType}[]` : nestedTargetType} }`;
524
+ }
525
+ }
526
+ const prevKey = path[i - 1];
527
+ const prevTarget = targets[i - 1];
528
+ if (prevKey && prevTarget) {
529
+ parts[parts.length - 1] = `${prevKey}: ${isMany[i - 1] ? `(Select${pascal(prevTarget)} & { ${key}: ${isMany[i] ? `${targetType}[]` : targetType} })[]` : `Select${pascal(prevTarget)} & { ${key}: ${isMany[i] ? `${targetType}[]` : targetType} }`}`;
530
+ }
531
+ break;
532
+ }
533
+ }
534
+ return `${type} & { ${parts.join("; ")} }`;
535
+ }
536
+ function buildIncludeSpec(path) {
537
+ if (path.length === 0)
538
+ return {};
539
+ if (path.length === 1)
540
+ return { [path[0]]: true };
541
+ let spec = true;
542
+ for (let i = path.length - 1;i > 0; i--) {
543
+ const key = path[i];
544
+ if (!key)
545
+ continue;
546
+ spec = { [key]: spec };
547
+ }
548
+ const rootKey = path[0];
549
+ return rootKey ? { [rootKey]: spec } : {};
550
+ }
551
+ function generateIncludeMethods(table, graph, opts, allTables) {
552
+ const methods = [];
553
+ const baseTableName = table.name;
554
+ if (opts.skipJunctionTables && isJunctionTable(table)) {
555
+ return methods;
556
+ }
557
+ const edges = graph[baseTableName] || {};
558
+ function explore(currentTable, path, isMany, targets, visited, depth) {
559
+ if (depth > opts.maxDepth)
560
+ return;
561
+ const currentEdges = graph[currentTable] || {};
562
+ for (const [key, edge] of Object.entries(currentEdges)) {
563
+ if (visited.has(edge.target))
564
+ continue;
565
+ if (opts.skipJunctionTables && allTables) {
566
+ const targetTable = allTables.find((t) => t.name === edge.target);
567
+ if (targetTable && isJunctionTable(targetTable)) {
568
+ continue;
569
+ }
570
+ }
571
+ const newPath = [...path, key];
572
+ const newIsMany = [...isMany, edge.kind === "many"];
573
+ const newTargets = [...targets, edge.target];
574
+ const methodSuffix = pathToMethodSuffix(newPath);
575
+ methods.push({
576
+ name: `list${methodSuffix}`,
577
+ path: newPath,
578
+ isMany: newIsMany,
579
+ targets: newTargets,
580
+ returnType: `(${buildReturnType(baseTableName, newPath, newIsMany, newTargets, graph)})[]`,
581
+ includeSpec: buildIncludeSpec(newPath)
582
+ });
583
+ methods.push({
584
+ name: `getByPk${methodSuffix}`,
585
+ path: newPath,
586
+ isMany: newIsMany,
587
+ targets: newTargets,
588
+ returnType: `${buildReturnType(baseTableName, newPath, newIsMany, newTargets, graph)} | null`,
589
+ includeSpec: buildIncludeSpec(newPath)
590
+ });
591
+ explore(edge.target, newPath, newIsMany, newTargets, new Set([...visited, edge.target]), depth + 1);
592
+ }
593
+ if (depth === 1 && Object.keys(currentEdges).length > 1 && Object.keys(currentEdges).length <= 3) {
594
+ const edgeEntries = Object.entries(currentEdges);
595
+ if (edgeEntries.length >= 2) {
596
+ for (let i = 0;i < edgeEntries.length - 1; i++) {
597
+ for (let j = i + 1;j < edgeEntries.length; j++) {
598
+ const entry1 = edgeEntries[i];
599
+ const entry2 = edgeEntries[j];
600
+ if (!entry1 || !entry2)
601
+ continue;
602
+ const [key1, edge1] = entry1;
603
+ const [key2, edge2] = entry2;
604
+ if (opts.skipJunctionTables && (edge1.target.includes("_") || edge2.target.includes("_"))) {
605
+ continue;
606
+ }
607
+ const combinedPath = [key1, key2];
608
+ const combinedSuffix = `With${pascal(key1)}And${pascal(key2)}`;
609
+ const type1 = `${key1}: ${edge1.kind === "many" ? `Select${pascal(edge1.target)}[]` : `Select${pascal(edge1.target)}`}`;
610
+ const type2 = `${key2}: ${edge2.kind === "many" ? `Select${pascal(edge2.target)}[]` : `Select${pascal(edge2.target)}`}`;
611
+ methods.push({
612
+ name: `list${combinedSuffix}`,
613
+ path: combinedPath,
614
+ isMany: [edge1.kind === "many", edge2.kind === "many"],
615
+ targets: [edge1.target, edge2.target],
616
+ returnType: `(Select${pascal(baseTableName)} & { ${type1}; ${type2} })[]`,
617
+ includeSpec: { [key1]: true, [key2]: true }
618
+ });
619
+ methods.push({
620
+ name: `getByPk${combinedSuffix}`,
621
+ path: combinedPath,
622
+ isMany: [edge1.kind === "many", edge2.kind === "many"],
623
+ targets: [edge1.target, edge2.target],
624
+ returnType: `(Select${pascal(baseTableName)} & { ${type1}; ${type2} }) | null`,
625
+ includeSpec: { [key1]: true, [key2]: true }
626
+ });
627
+ }
628
+ }
629
+ }
630
+ }
631
+ }
632
+ explore(baseTableName, [], [], [], new Set([baseTableName]), 1);
633
+ return methods;
634
+ }
635
+ var init_emit_include_methods = __esm(() => {
636
+ init_utils();
637
+ });
638
+
488
639
  // src/emit-sdk-contract.ts
489
640
  var exports_emit_sdk_contract = {};
490
641
  __export(exports_emit_sdk_contract, {
@@ -492,7 +643,7 @@ __export(exports_emit_sdk_contract, {
492
643
  generateUnifiedContract: () => generateUnifiedContract,
493
644
  emitUnifiedContract: () => emitUnifiedContract
494
645
  });
495
- function generateUnifiedContract(model, config) {
646
+ function generateUnifiedContract(model, config, graph) {
496
647
  const resources = [];
497
648
  const relationships = [];
498
649
  const tables = model && model.tables ? Object.values(model.tables) : [];
@@ -500,7 +651,7 @@ function generateUnifiedContract(model, config) {
500
651
  console.log(`[SDK Contract] Processing ${tables.length} tables`);
501
652
  }
502
653
  for (const table of tables) {
503
- resources.push(generateResourceWithSDK(table, model));
654
+ resources.push(generateResourceWithSDK(table, model, graph, config));
504
655
  for (const fk of table.fks) {
505
656
  relationships.push({
506
657
  from: table.name,
@@ -618,7 +769,7 @@ function generateSDKAuthExamples(auth) {
618
769
  });
619
770
  return examples;
620
771
  }
621
- function generateResourceWithSDK(table, model) {
772
+ function generateResourceWithSDK(table, model, graph, config) {
622
773
  const Type = pascal(table.name);
623
774
  const tableName = table.name;
624
775
  const basePath = `/v1/${tableName}`;
@@ -640,11 +791,6 @@ const filtered = await sdk.${tableName}.list({
640
791
  ${table.columns[0]?.name || "field"}_like: 'search',
641
792
  order_by: '${table.columns[0]?.name || "created_at"}',
642
793
  order_dir: 'desc'
643
- });
644
-
645
- // With related data
646
- const withRelations = await sdk.${tableName}.list({
647
- include: '${table.fks[0]?.toTable || "related_table"}'
648
794
  });`,
649
795
  correspondsTo: `GET ${basePath}`
650
796
  });
@@ -658,16 +804,11 @@ const withRelations = await sdk.${tableName}.list({
658
804
  if (hasSinglePK) {
659
805
  sdkMethods.push({
660
806
  name: "getByPk",
661
- signature: `getByPk(${pkField}: string, params?: GetParams): Promise<${Type} | null>`,
807
+ signature: `getByPk(${pkField}: string): Promise<${Type} | null>`,
662
808
  description: `Get a single ${tableName} by primary key`,
663
809
  example: `// Get by ID
664
810
  const item = await sdk.${tableName}.getByPk('123e4567-e89b-12d3-a456-426614174000');
665
811
 
666
- // With related data
667
- const withRelations = await sdk.${tableName}.getByPk('123', {
668
- include: '${table.fks[0]?.toTable || "related_table"}'
669
- });
670
-
671
812
  // Check if exists
672
813
  if (item === null) {
673
814
  console.log('Not found');
@@ -678,9 +819,6 @@ if (item === null) {
678
819
  method: "GET",
679
820
  path: `${basePath}/:${pkField}`,
680
821
  description: `Get ${tableName} by ID`,
681
- queryParameters: {
682
- include: "string - Comma-separated list of related resources"
683
- },
684
822
  responseBody: `${Type}`
685
823
  });
686
824
  }
@@ -743,6 +881,31 @@ console.log('Deleted:', deleted);`,
743
881
  responseBody: `${Type}`
744
882
  });
745
883
  }
884
+ if (graph && config) {
885
+ const allTables = model && model.tables ? Object.values(model.tables) : undefined;
886
+ const includeMethods = generateIncludeMethods(table, graph, {
887
+ maxDepth: config.includeMethodsDepth ?? 2,
888
+ skipJunctionTables: config.skipJunctionTables ?? true
889
+ }, allTables);
890
+ for (const method of includeMethods) {
891
+ const isGetByPk = method.name.startsWith("getByPk");
892
+ const exampleCall = isGetByPk ? `const result = await sdk.${tableName}.${method.name}('123e4567-e89b-12d3-a456-426614174000');` : `const results = await sdk.${tableName}.${method.name}();
893
+
894
+ // With filters and pagination
895
+ const filtered = await sdk.${tableName}.${method.name}({
896
+ limit: 20,
897
+ offset: 0,
898
+ where: { /* filter conditions */ }
899
+ });`;
900
+ sdkMethods.push({
901
+ name: method.name,
902
+ signature: `${method.name}(${isGetByPk ? `${pkField}: string` : "params?: ListParams"}): ${method.returnType}`,
903
+ description: `Get ${tableName} with included ${method.path.join(", ")} data`,
904
+ example: exampleCall,
905
+ correspondsTo: `POST ${basePath}/list`
906
+ });
907
+ }
908
+ }
746
909
  const fields = table.columns.map((col) => generateFieldContract(col, table));
747
910
  return {
748
911
  name: Type,
@@ -891,8 +1054,7 @@ function generateQueryParams(table) {
891
1054
  limit: "number - Max records to return (default: 50)",
892
1055
  offset: "number - Records to skip",
893
1056
  order_by: "string - Field to sort by",
894
- order_dir: "'asc' | 'desc' - Sort direction",
895
- include: "string - Related resources to include"
1057
+ order_dir: "'asc' | 'desc' - Sort direction"
896
1058
  };
897
1059
  let filterCount = 0;
898
1060
  for (const col of table.columns) {
@@ -1079,8 +1241,8 @@ function generateUnifiedContractMarkdown(contract) {
1079
1241
  return lines.join(`
1080
1242
  `);
1081
1243
  }
1082
- function emitUnifiedContract(model, config) {
1083
- const contract = generateUnifiedContract(model, config);
1244
+ function emitUnifiedContract(model, config, graph) {
1245
+ const contract = generateUnifiedContract(model, config, graph);
1084
1246
  const contractJson = JSON.stringify(contract, null, 2);
1085
1247
  return `/**
1086
1248
  * Unified API & SDK Contract
@@ -1136,6 +1298,7 @@ import type * as Types from './client/types';
1136
1298
  }
1137
1299
  var init_emit_sdk_contract = __esm(() => {
1138
1300
  init_utils();
1301
+ init_emit_include_methods();
1139
1302
  });
1140
1303
 
1141
1304
  // src/index.ts
@@ -1651,156 +1814,8 @@ ${hasAuth ? `
1651
1814
 
1652
1815
  // src/emit-client.ts
1653
1816
  init_utils();
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) {
1817
+ init_emit_include_methods();
1818
+ function emitClient(table, graph, opts, model) {
1804
1819
  const Type = pascal(table.name);
1805
1820
  const ext = opts.useJsExtensions ? ".js" : "";
1806
1821
  const pkCols = Array.isArray(table.pk) ? table.pk : table.pk ? [table.pk] : [];
@@ -1808,10 +1823,11 @@ function emitClient(table, graph, opts) {
1808
1823
  const hasCompositePk = safePk.length > 1;
1809
1824
  const pkType = hasCompositePk ? `{ ${safePk.map((c) => `${c}: string`).join("; ")} }` : `string`;
1810
1825
  const pkPathExpr = hasCompositePk ? safePk.map((c) => `pk.${c}`).join(` + "/" + `) : `pk`;
1826
+ const allTables = model ? Object.values(model.tables) : undefined;
1811
1827
  const includeMethods = generateIncludeMethods(table, graph, {
1812
1828
  maxDepth: opts.includeMethodsDepth ?? 2,
1813
1829
  skipJunctionTables: opts.skipJunctionTables ?? true
1814
- });
1830
+ }, allTables);
1815
1831
  const importedTypes = new Set;
1816
1832
  importedTypes.add(table.name);
1817
1833
  for (const method of includeMethods) {
@@ -1821,7 +1837,7 @@ function emitClient(table, graph, opts) {
1821
1837
  }
1822
1838
  const typeImports = `import type { Insert${Type}, Update${Type}, Select${Type} } from "./types/${table.name}${ext}";`;
1823
1839
  const otherTableImports = [];
1824
- for (const target of importedTypes) {
1840
+ for (const target of Array.from(importedTypes)) {
1825
1841
  if (target !== table.name) {
1826
1842
  otherTableImports.push(`import type { Select${pascal(target)} } from "./types/${target}${ext}";`);
1827
1843
  }
@@ -3820,11 +3836,11 @@ async function generate(configPath) {
3820
3836
  files.push({ path: join(clientDir, "base-client.ts"), content: emitBaseClient() });
3821
3837
  files.push({
3822
3838
  path: join(serverDir, "include-builder.ts"),
3823
- content: emitIncludeBuilder(graph, cfg.includeDepthLimit || 3)
3839
+ content: emitIncludeBuilder(graph, cfg.includeMethodsDepth || 2)
3824
3840
  });
3825
3841
  files.push({
3826
3842
  path: join(serverDir, "include-loader.ts"),
3827
- content: emitIncludeLoader(graph, model, cfg.includeDepthLimit || 3, cfg.useJsExtensions)
3843
+ content: emitIncludeLoader(graph, model, cfg.includeMethodsDepth || 2, cfg.useJsExtensions)
3828
3844
  });
3829
3845
  files.push({ path: join(serverDir, "logger.ts"), content: emitLogger() });
3830
3846
  if (normalizedAuth?.strategy && normalizedAuth.strategy !== "none") {
@@ -3850,7 +3866,7 @@ async function generate(configPath) {
3850
3866
  if (serverFramework === "hono") {
3851
3867
  routeContent = emitHonoRoutes(table, graph, {
3852
3868
  softDeleteColumn: cfg.softDeleteColumn || null,
3853
- includeDepthLimit: cfg.includeDepthLimit || 3,
3869
+ includeDepthLimit: cfg.includeMethodsDepth || 2,
3854
3870
  authStrategy: normalizedAuth?.strategy,
3855
3871
  useJsExtensions: cfg.useJsExtensions
3856
3872
  });
@@ -3867,7 +3883,7 @@ async function generate(configPath) {
3867
3883
  useJsExtensions: cfg.useJsExtensionsClient,
3868
3884
  includeMethodsDepth: cfg.includeMethodsDepth ?? 2,
3869
3885
  skipJunctionTables: cfg.skipJunctionTables ?? true
3870
- })
3886
+ }, model)
3871
3887
  });
3872
3888
  }
3873
3889
  files.push({
@@ -3896,7 +3912,7 @@ async function generate(configPath) {
3896
3912
  if (process.env.SDK_DEBUG) {
3897
3913
  console.log(`[Index] Model has ${Object.keys(model.tables || {}).length} tables before contract generation`);
3898
3914
  }
3899
- const contract = generateUnifiedContract2(model, cfg);
3915
+ const contract = generateUnifiedContract2(model, cfg, graph);
3900
3916
  files.push({
3901
3917
  path: join(serverDir, "CONTRACT.md"),
3902
3918
  content: generateUnifiedContractMarkdown2(contract)
package/dist/types.d.ts CHANGED
@@ -24,7 +24,7 @@ export interface Config {
24
24
  outServer?: string;
25
25
  outClient?: string;
26
26
  softDeleteColumn?: string | null;
27
- includeDepthLimit?: number;
27
+ dateType?: "date" | "string";
28
28
  includeMethodsDepth?: number;
29
29
  skipJunctionTables?: boolean;
30
30
  serverFramework?: "hono" | "express" | "fastify";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postgresdk",
3
- "version": "0.8.0",
3
+ "version": "0.9.1",
4
4
  "description": "Generate a typed server/client SDK from a Postgres schema (includes, Zod, Hono).",
5
5
  "type": "module",
6
6
  "bin": {