postgresdk 0.18.9 → 0.18.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/cli.js +84 -10
- package/dist/emit-include-resolver.d.ts +9 -0
- package/dist/index.js +84 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -567,7 +567,7 @@ const deleted = await sdk.users.delete(123);
|
|
|
567
567
|
|
|
568
568
|
#### Relationships & Eager Loading
|
|
569
569
|
|
|
570
|
-
Automatically handles relationships with the `include` parameter:
|
|
570
|
+
Automatically handles relationships with the `include` parameter. **Type inference works automatically** - no manual casts needed:
|
|
571
571
|
|
|
572
572
|
```typescript
|
|
573
573
|
// 1:N relationship - Get authors with their books
|
|
@@ -575,12 +575,14 @@ const authorsResult = await sdk.authors.list({
|
|
|
575
575
|
include: { books: true }
|
|
576
576
|
});
|
|
577
577
|
const authors = authorsResult.data;
|
|
578
|
+
// ✅ authors[0].books is automatically typed as SelectBooks[]
|
|
578
579
|
|
|
579
580
|
// M:N relationship - Get books with their tags
|
|
580
581
|
const booksResult = await sdk.books.list({
|
|
581
582
|
include: { tags: true }
|
|
582
583
|
});
|
|
583
584
|
const books = booksResult.data;
|
|
585
|
+
// ✅ books[0].tags is automatically typed as SelectTags[]
|
|
584
586
|
|
|
585
587
|
// Nested includes - Get authors with books and their tags
|
|
586
588
|
const nestedResult = await sdk.authors.list({
|
|
@@ -591,6 +593,7 @@ const nestedResult = await sdk.authors.list({
|
|
|
591
593
|
}
|
|
592
594
|
});
|
|
593
595
|
const authorsWithBooksAndTags = nestedResult.data;
|
|
596
|
+
// ✅ TypeScript knows: data[0].books[0].tags exists and is SelectTags[]
|
|
594
597
|
```
|
|
595
598
|
|
|
596
599
|
**Typed Include Methods:**
|
package/dist/cli.js
CHANGED
|
@@ -2899,6 +2899,73 @@ function toPascal(s) {
|
|
|
2899
2899
|
return s.split(/[_\s-]+/).map((w) => w?.[0] ? w[0].toUpperCase() + w.slice(1) : "").join("");
|
|
2900
2900
|
}
|
|
2901
2901
|
|
|
2902
|
+
// src/emit-include-resolver.ts
|
|
2903
|
+
init_utils();
|
|
2904
|
+
function emitIncludeResolver(graph, useJsExtensions) {
|
|
2905
|
+
const ext = useJsExtensions ? ".js" : "";
|
|
2906
|
+
let out = `/**
|
|
2907
|
+
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
2908
|
+
*
|
|
2909
|
+
* Type helpers for automatic include inference.
|
|
2910
|
+
* These types transform IncludeSpec into the actual return type shape.
|
|
2911
|
+
*/
|
|
2912
|
+
`;
|
|
2913
|
+
const tables = Object.keys(graph);
|
|
2914
|
+
for (const table of tables) {
|
|
2915
|
+
out += `import type { Select${pascal(table)} } from "./types/${table}${ext}";
|
|
2916
|
+
`;
|
|
2917
|
+
}
|
|
2918
|
+
for (const table of tables) {
|
|
2919
|
+
out += `import type { ${pascal(table)}IncludeSpec } from "./include-spec${ext}";
|
|
2920
|
+
`;
|
|
2921
|
+
}
|
|
2922
|
+
out += `
|
|
2923
|
+
`;
|
|
2924
|
+
for (const table of tables) {
|
|
2925
|
+
const Type = pascal(table);
|
|
2926
|
+
const edges = graph[table] || {};
|
|
2927
|
+
const edgeEntries = Object.entries(edges);
|
|
2928
|
+
if (edgeEntries.length === 0) {
|
|
2929
|
+
out += `export type ${Type}WithIncludes<TInclude extends ${Type}IncludeSpec> = Select${Type};
|
|
2930
|
+
|
|
2931
|
+
`;
|
|
2932
|
+
continue;
|
|
2933
|
+
}
|
|
2934
|
+
out += `export type ${Type}WithIncludes<TInclude extends ${Type}IncludeSpec> =
|
|
2935
|
+
Select${Type} & {
|
|
2936
|
+
[K in keyof TInclude as TInclude[K] extends false | undefined ? never : K]:`;
|
|
2937
|
+
for (let i = 0;i < edgeEntries.length; i++) {
|
|
2938
|
+
const [relKey, edge] = edgeEntries[i];
|
|
2939
|
+
if (!relKey || !edge)
|
|
2940
|
+
continue;
|
|
2941
|
+
const targetType = pascal(edge.target);
|
|
2942
|
+
const isLast = i === edgeEntries.length - 1;
|
|
2943
|
+
out += `
|
|
2944
|
+
K extends '${relKey}' ? `;
|
|
2945
|
+
if (edge.kind === "many") {
|
|
2946
|
+
out += `(
|
|
2947
|
+
TInclude[K] extends { include: infer U extends ${targetType}IncludeSpec }
|
|
2948
|
+
? Array<${targetType}WithIncludes<U>>
|
|
2949
|
+
: Select${targetType}[]
|
|
2950
|
+
)`;
|
|
2951
|
+
} else {
|
|
2952
|
+
out += `(
|
|
2953
|
+
TInclude[K] extends { include: infer U extends ${targetType}IncludeSpec }
|
|
2954
|
+
? ${targetType}WithIncludes<U>
|
|
2955
|
+
: Select${targetType}
|
|
2956
|
+
)`;
|
|
2957
|
+
}
|
|
2958
|
+
out += ` :${isLast ? `
|
|
2959
|
+
never` : ""}`;
|
|
2960
|
+
}
|
|
2961
|
+
out += `
|
|
2962
|
+
};
|
|
2963
|
+
|
|
2964
|
+
`;
|
|
2965
|
+
}
|
|
2966
|
+
return out;
|
|
2967
|
+
}
|
|
2968
|
+
|
|
2902
2969
|
// src/emit-include-builder.ts
|
|
2903
2970
|
function emitIncludeBuilder(graph, maxDepth) {
|
|
2904
2971
|
return `/**
|
|
@@ -3462,6 +3529,7 @@ function emitClient(table, graph, opts, model) {
|
|
|
3462
3529
|
const typeImports = `import type { Insert${Type}, Update${Type}, Select${Type} } from "./types/${table.name}${ext}";`;
|
|
3463
3530
|
const includeSpecTypes = [table.name, ...Array.from(importedTypes).filter((t) => t !== table.name)];
|
|
3464
3531
|
const includeSpecImport = `import type { ${includeSpecTypes.map((t) => `${pascal(t)}IncludeSpec`).join(", ")} } from "./include-spec${ext}";`;
|
|
3532
|
+
const includeResolverImport = `import type { ${Type}WithIncludes } from "./include-resolver${ext}";`;
|
|
3465
3533
|
const otherTableImports = [];
|
|
3466
3534
|
for (const target of Array.from(importedTypes)) {
|
|
3467
3535
|
if (target !== table.name) {
|
|
@@ -3655,6 +3723,7 @@ import type { Where } from "./where-types${ext}";
|
|
|
3655
3723
|
import type { PaginatedResponse } from "./types/shared${ext}";
|
|
3656
3724
|
${typeImports}
|
|
3657
3725
|
${includeSpecImport}
|
|
3726
|
+
${includeResolverImport}
|
|
3658
3727
|
${otherTableImports.join(`
|
|
3659
3728
|
`)}
|
|
3660
3729
|
|
|
@@ -3845,14 +3914,17 @@ ${hasJsonbColumns ? ` /**
|
|
|
3845
3914
|
* @param params.order - Sort direction(s): "asc" or "desc"
|
|
3846
3915
|
* @param params.limit - Maximum number of records to return (default: 50, max: 1000)
|
|
3847
3916
|
* @param params.offset - Number of records to skip for pagination
|
|
3848
|
-
* @param params.include - Related records to include (
|
|
3849
|
-
* @returns Paginated results with all fields
|
|
3917
|
+
* @param params.include - Related records to include (return type automatically infers included relations)
|
|
3918
|
+
* @returns Paginated results with all fields (and included relations if specified)
|
|
3850
3919
|
* @example
|
|
3851
3920
|
* // With JSONB type override:
|
|
3852
3921
|
* const users = await client.list<{ metadata: Metadata }>({ where: { status: 'active' } });
|
|
3922
|
+
* // With automatic include inference:
|
|
3923
|
+
* const users = await client.list({ include: { posts: true } });
|
|
3924
|
+
* // users[0].posts is automatically typed
|
|
3853
3925
|
*/
|
|
3854
|
-
async list<TJsonb extends Partial<Select${Type}> = {}>(params?: {
|
|
3855
|
-
include?:
|
|
3926
|
+
async list<TJsonb extends Partial<Select${Type}> = {}, TInclude extends ${Type}IncludeSpec = {}>(params?: {
|
|
3927
|
+
include?: TInclude;
|
|
3856
3928
|
limit?: number;
|
|
3857
3929
|
offset?: number;
|
|
3858
3930
|
where?: Where<Select${Type}<TJsonb>>;${hasVectorColumns ? `
|
|
@@ -3864,7 +3936,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3864
3936
|
};` : ""}
|
|
3865
3937
|
orderBy?: string | string[];
|
|
3866
3938
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3867
|
-
}): Promise<PaginatedResponse
|
|
3939
|
+
}): Promise<PaginatedResponse<${Type}WithIncludes<TInclude>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
3868
3940
|
async list<TJsonb extends Partial<Select${Type}> = {}>(params?: {
|
|
3869
3941
|
include?: ${Type}IncludeSpec;
|
|
3870
3942
|
select?: string[];
|
|
@@ -3930,11 +4002,11 @@ ${hasJsonbColumns ? ` /**
|
|
|
3930
4002
|
* @param params.order - Sort direction(s): "asc" or "desc"
|
|
3931
4003
|
* @param params.limit - Maximum number of records to return (default: 50, max: 1000)
|
|
3932
4004
|
* @param params.offset - Number of records to skip for pagination
|
|
3933
|
-
* @param params.include - Related records to include (
|
|
3934
|
-
* @returns Paginated results with all fields
|
|
4005
|
+
* @param params.include - Related records to include (return type automatically infers included relations)
|
|
4006
|
+
* @returns Paginated results with all fields (and included relations if specified)
|
|
3935
4007
|
*/
|
|
3936
|
-
async list(params?: {
|
|
3937
|
-
include?:
|
|
4008
|
+
async list<TInclude extends ${Type}IncludeSpec = {}>(params?: {
|
|
4009
|
+
include?: TInclude;
|
|
3938
4010
|
limit?: number;
|
|
3939
4011
|
offset?: number;
|
|
3940
4012
|
where?: Where<Select${Type}>;${hasVectorColumns ? `
|
|
@@ -3946,7 +4018,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3946
4018
|
};` : ""}
|
|
3947
4019
|
orderBy?: string | string[];
|
|
3948
4020
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3949
|
-
}): Promise<PaginatedResponse
|
|
4021
|
+
}): Promise<PaginatedResponse<${Type}WithIncludes<TInclude>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
3950
4022
|
async list(params?: {
|
|
3951
4023
|
include?: ${Type}IncludeSpec;
|
|
3952
4024
|
select?: string[];
|
|
@@ -7119,6 +7191,8 @@ async function generate(configPath) {
|
|
|
7119
7191
|
const includeSpec = emitIncludeSpec(graph);
|
|
7120
7192
|
files.push({ path: join(serverDir, "include-spec.ts"), content: includeSpec });
|
|
7121
7193
|
files.push({ path: join(clientDir, "include-spec.ts"), content: includeSpec });
|
|
7194
|
+
const includeResolver = emitIncludeResolver(graph, cfg.useJsExtensions);
|
|
7195
|
+
files.push({ path: join(clientDir, "include-resolver.ts"), content: includeResolver });
|
|
7122
7196
|
files.push({ path: join(clientDir, "params", "shared.ts"), content: emitSharedParamsZod() });
|
|
7123
7197
|
files.push({ path: join(clientDir, "types", "shared.ts"), content: emitSharedTypes() });
|
|
7124
7198
|
files.push({ path: join(clientDir, "base-client.ts"), content: emitBaseClient() });
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Graph } from "./rel-classify";
|
|
2
|
+
/**
|
|
3
|
+
* Generate TypeScript types that resolve IncludeSpec to actual return types
|
|
4
|
+
*
|
|
5
|
+
* This allows automatic type inference:
|
|
6
|
+
* const result = await sdk.captures.list({ include: { website: true } });
|
|
7
|
+
* // result.data[0].website is typed as SelectWebsites
|
|
8
|
+
*/
|
|
9
|
+
export declare function emitIncludeResolver(graph: Graph, useJsExtensions?: boolean): string;
|
package/dist/index.js
CHANGED
|
@@ -1938,6 +1938,73 @@ function toPascal(s) {
|
|
|
1938
1938
|
return s.split(/[_\s-]+/).map((w) => w?.[0] ? w[0].toUpperCase() + w.slice(1) : "").join("");
|
|
1939
1939
|
}
|
|
1940
1940
|
|
|
1941
|
+
// src/emit-include-resolver.ts
|
|
1942
|
+
init_utils();
|
|
1943
|
+
function emitIncludeResolver(graph, useJsExtensions) {
|
|
1944
|
+
const ext = useJsExtensions ? ".js" : "";
|
|
1945
|
+
let out = `/**
|
|
1946
|
+
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
1947
|
+
*
|
|
1948
|
+
* Type helpers for automatic include inference.
|
|
1949
|
+
* These types transform IncludeSpec into the actual return type shape.
|
|
1950
|
+
*/
|
|
1951
|
+
`;
|
|
1952
|
+
const tables = Object.keys(graph);
|
|
1953
|
+
for (const table of tables) {
|
|
1954
|
+
out += `import type { Select${pascal(table)} } from "./types/${table}${ext}";
|
|
1955
|
+
`;
|
|
1956
|
+
}
|
|
1957
|
+
for (const table of tables) {
|
|
1958
|
+
out += `import type { ${pascal(table)}IncludeSpec } from "./include-spec${ext}";
|
|
1959
|
+
`;
|
|
1960
|
+
}
|
|
1961
|
+
out += `
|
|
1962
|
+
`;
|
|
1963
|
+
for (const table of tables) {
|
|
1964
|
+
const Type = pascal(table);
|
|
1965
|
+
const edges = graph[table] || {};
|
|
1966
|
+
const edgeEntries = Object.entries(edges);
|
|
1967
|
+
if (edgeEntries.length === 0) {
|
|
1968
|
+
out += `export type ${Type}WithIncludes<TInclude extends ${Type}IncludeSpec> = Select${Type};
|
|
1969
|
+
|
|
1970
|
+
`;
|
|
1971
|
+
continue;
|
|
1972
|
+
}
|
|
1973
|
+
out += `export type ${Type}WithIncludes<TInclude extends ${Type}IncludeSpec> =
|
|
1974
|
+
Select${Type} & {
|
|
1975
|
+
[K in keyof TInclude as TInclude[K] extends false | undefined ? never : K]:`;
|
|
1976
|
+
for (let i = 0;i < edgeEntries.length; i++) {
|
|
1977
|
+
const [relKey, edge] = edgeEntries[i];
|
|
1978
|
+
if (!relKey || !edge)
|
|
1979
|
+
continue;
|
|
1980
|
+
const targetType = pascal(edge.target);
|
|
1981
|
+
const isLast = i === edgeEntries.length - 1;
|
|
1982
|
+
out += `
|
|
1983
|
+
K extends '${relKey}' ? `;
|
|
1984
|
+
if (edge.kind === "many") {
|
|
1985
|
+
out += `(
|
|
1986
|
+
TInclude[K] extends { include: infer U extends ${targetType}IncludeSpec }
|
|
1987
|
+
? Array<${targetType}WithIncludes<U>>
|
|
1988
|
+
: Select${targetType}[]
|
|
1989
|
+
)`;
|
|
1990
|
+
} else {
|
|
1991
|
+
out += `(
|
|
1992
|
+
TInclude[K] extends { include: infer U extends ${targetType}IncludeSpec }
|
|
1993
|
+
? ${targetType}WithIncludes<U>
|
|
1994
|
+
: Select${targetType}
|
|
1995
|
+
)`;
|
|
1996
|
+
}
|
|
1997
|
+
out += ` :${isLast ? `
|
|
1998
|
+
never` : ""}`;
|
|
1999
|
+
}
|
|
2000
|
+
out += `
|
|
2001
|
+
};
|
|
2002
|
+
|
|
2003
|
+
`;
|
|
2004
|
+
}
|
|
2005
|
+
return out;
|
|
2006
|
+
}
|
|
2007
|
+
|
|
1941
2008
|
// src/emit-include-builder.ts
|
|
1942
2009
|
function emitIncludeBuilder(graph, maxDepth) {
|
|
1943
2010
|
return `/**
|
|
@@ -2501,6 +2568,7 @@ function emitClient(table, graph, opts, model) {
|
|
|
2501
2568
|
const typeImports = `import type { Insert${Type}, Update${Type}, Select${Type} } from "./types/${table.name}${ext}";`;
|
|
2502
2569
|
const includeSpecTypes = [table.name, ...Array.from(importedTypes).filter((t) => t !== table.name)];
|
|
2503
2570
|
const includeSpecImport = `import type { ${includeSpecTypes.map((t) => `${pascal(t)}IncludeSpec`).join(", ")} } from "./include-spec${ext}";`;
|
|
2571
|
+
const includeResolverImport = `import type { ${Type}WithIncludes } from "./include-resolver${ext}";`;
|
|
2504
2572
|
const otherTableImports = [];
|
|
2505
2573
|
for (const target of Array.from(importedTypes)) {
|
|
2506
2574
|
if (target !== table.name) {
|
|
@@ -2694,6 +2762,7 @@ import type { Where } from "./where-types${ext}";
|
|
|
2694
2762
|
import type { PaginatedResponse } from "./types/shared${ext}";
|
|
2695
2763
|
${typeImports}
|
|
2696
2764
|
${includeSpecImport}
|
|
2765
|
+
${includeResolverImport}
|
|
2697
2766
|
${otherTableImports.join(`
|
|
2698
2767
|
`)}
|
|
2699
2768
|
|
|
@@ -2884,14 +2953,17 @@ ${hasJsonbColumns ? ` /**
|
|
|
2884
2953
|
* @param params.order - Sort direction(s): "asc" or "desc"
|
|
2885
2954
|
* @param params.limit - Maximum number of records to return (default: 50, max: 1000)
|
|
2886
2955
|
* @param params.offset - Number of records to skip for pagination
|
|
2887
|
-
* @param params.include - Related records to include (
|
|
2888
|
-
* @returns Paginated results with all fields
|
|
2956
|
+
* @param params.include - Related records to include (return type automatically infers included relations)
|
|
2957
|
+
* @returns Paginated results with all fields (and included relations if specified)
|
|
2889
2958
|
* @example
|
|
2890
2959
|
* // With JSONB type override:
|
|
2891
2960
|
* const users = await client.list<{ metadata: Metadata }>({ where: { status: 'active' } });
|
|
2961
|
+
* // With automatic include inference:
|
|
2962
|
+
* const users = await client.list({ include: { posts: true } });
|
|
2963
|
+
* // users[0].posts is automatically typed
|
|
2892
2964
|
*/
|
|
2893
|
-
async list<TJsonb extends Partial<Select${Type}> = {}>(params?: {
|
|
2894
|
-
include?:
|
|
2965
|
+
async list<TJsonb extends Partial<Select${Type}> = {}, TInclude extends ${Type}IncludeSpec = {}>(params?: {
|
|
2966
|
+
include?: TInclude;
|
|
2895
2967
|
limit?: number;
|
|
2896
2968
|
offset?: number;
|
|
2897
2969
|
where?: Where<Select${Type}<TJsonb>>;${hasVectorColumns ? `
|
|
@@ -2903,7 +2975,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
2903
2975
|
};` : ""}
|
|
2904
2976
|
orderBy?: string | string[];
|
|
2905
2977
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
2906
|
-
}): Promise<PaginatedResponse
|
|
2978
|
+
}): Promise<PaginatedResponse<${Type}WithIncludes<TInclude>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
2907
2979
|
async list<TJsonb extends Partial<Select${Type}> = {}>(params?: {
|
|
2908
2980
|
include?: ${Type}IncludeSpec;
|
|
2909
2981
|
select?: string[];
|
|
@@ -2969,11 +3041,11 @@ ${hasJsonbColumns ? ` /**
|
|
|
2969
3041
|
* @param params.order - Sort direction(s): "asc" or "desc"
|
|
2970
3042
|
* @param params.limit - Maximum number of records to return (default: 50, max: 1000)
|
|
2971
3043
|
* @param params.offset - Number of records to skip for pagination
|
|
2972
|
-
* @param params.include - Related records to include (
|
|
2973
|
-
* @returns Paginated results with all fields
|
|
3044
|
+
* @param params.include - Related records to include (return type automatically infers included relations)
|
|
3045
|
+
* @returns Paginated results with all fields (and included relations if specified)
|
|
2974
3046
|
*/
|
|
2975
|
-
async list(params?: {
|
|
2976
|
-
include?:
|
|
3047
|
+
async list<TInclude extends ${Type}IncludeSpec = {}>(params?: {
|
|
3048
|
+
include?: TInclude;
|
|
2977
3049
|
limit?: number;
|
|
2978
3050
|
offset?: number;
|
|
2979
3051
|
where?: Where<Select${Type}>;${hasVectorColumns ? `
|
|
@@ -2985,7 +3057,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
2985
3057
|
};` : ""}
|
|
2986
3058
|
orderBy?: string | string[];
|
|
2987
3059
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
2988
|
-
}): Promise<PaginatedResponse
|
|
3060
|
+
}): Promise<PaginatedResponse<${Type}WithIncludes<TInclude>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
2989
3061
|
async list(params?: {
|
|
2990
3062
|
include?: ${Type}IncludeSpec;
|
|
2991
3063
|
select?: string[];
|
|
@@ -6158,6 +6230,8 @@ async function generate(configPath) {
|
|
|
6158
6230
|
const includeSpec = emitIncludeSpec(graph);
|
|
6159
6231
|
files.push({ path: join(serverDir, "include-spec.ts"), content: includeSpec });
|
|
6160
6232
|
files.push({ path: join(clientDir, "include-spec.ts"), content: includeSpec });
|
|
6233
|
+
const includeResolver = emitIncludeResolver(graph, cfg.useJsExtensions);
|
|
6234
|
+
files.push({ path: join(clientDir, "include-resolver.ts"), content: includeResolver });
|
|
6161
6235
|
files.push({ path: join(clientDir, "params", "shared.ts"), content: emitSharedParamsZod() });
|
|
6162
6236
|
files.push({ path: join(clientDir, "types", "shared.ts"), content: emitSharedTypes() });
|
|
6163
6237
|
files.push({ path: join(clientDir, "base-client.ts"), content: emitBaseClient() });
|