Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type { OrmClientConfig } from "./client";
import { UserModel } from "./models/user";
import { PostModel } from "./models/post";
export type { OrmClientConfig, QueryResult, GraphQLError, GraphQLAdapter } from "./client";
export { GraphQLRequestError } from "./client";
export { GraphQLRequestError, FetchAdapter } from "./client";
export { QueryBuilder } from "./query-builder";
export * from "./select-types";
export * from "./models";
Expand Down Expand Up @@ -59,7 +59,7 @@ import { UserModel } from "./models/user";
import { createQueryOperations } from "./query";
import { createMutationOperations } from "./mutation";
export type { OrmClientConfig, QueryResult, GraphQLError, GraphQLAdapter } from "./client";
export { GraphQLRequestError } from "./client";
export { GraphQLRequestError, FetchAdapter } from "./client";
export { QueryBuilder } from "./query-builder";
export * from "./select-types";
export * from "./models";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1003,3 +1003,69 @@ describe('edge cases', () => {
);
});
});

// ============================================================================
// Tests - Table Field Argument Input Types (Bucket-style computed fields)
// ============================================================================

describe('table field argument input types', () => {
it('emits Input types referenced only by table-field args', () => {
// Bucket-style computed field: requestBulkUploadUrls(files: [FooBulkUploadFileInput!]!)
const bucketTable = createTable({
name: 'FooBucket',
fields: [
{ name: 'id', type: fieldTypes.uuid },
{
name: 'requestBulkUploadUrls',
type: { gqlType: 'String', isArray: true } as FieldType,
args: [
{
name: 'files',
type: createNonNull(
createList(
createNonNull(
createTypeRef('INPUT_OBJECT', 'FooBulkUploadFileInput'),
),
),
),
isRequired: true,
},
],
},
],
});

const registry = createTypeRegistry({
FooBulkUploadFileInput: {
kind: 'INPUT_OBJECT',
name: 'FooBulkUploadFileInput',
inputFields: [
{
name: 'fileName',
type: createNonNull(createTypeRef('SCALAR', 'String')),
},
{
name: 'contentType',
type: createTypeRef('SCALAR', 'String'),
},
{
name: 'sizeBytes',
type: createNonNull(createTypeRef('SCALAR', 'Int')),
},
],
},
});

// No custom operations — input type is referenced only via the
// computed field's `args`. Without the field-arg seeding fix, this
// type would be inlined into FooBucketSelect but never declared.
const result = generateInputTypesFile(registry, new Set(), [bucketTable]);

expect(result.content).toContain(
'export interface FooBulkUploadFileInput {',
);
expect(result.content).toContain('fileName: string;');
expect(result.content).toContain('contentType?: string;');
expect(result.content).toContain('sizeBytes: number;');
});
});
6 changes: 5 additions & 1 deletion graphql/codegen/src/core/codegen/orm/client-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ export function generateCreateClientFile(
typeExportDecl.exportKind = 'type';
statements.push(typeExportDecl);

// export { GraphQLRequestError } from './client';
// export { GraphQLRequestError, FetchAdapter } from './client';
statements.push(
t.exportNamedDeclaration(
null,
Expand All @@ -205,6 +205,10 @@ export function generateCreateClientFile(
t.identifier('GraphQLRequestError'),
t.identifier('GraphQLRequestError'),
),
t.exportSpecifier(
t.identifier('FetchAdapter'),
t.identifier('FetchAdapter'),
),
],
t.stringLiteral('./client'),
),
Expand Down
36 changes: 36 additions & 0 deletions graphql/codegen/src/core/codegen/orm/input-types-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1993,6 +1993,33 @@ function collectFilterExtraInputTypes(
return extraTypes;
}

/**
* Collect input type names referenced by table field arguments.
*
* Computed fields (e.g., `requestBulkUploadUrls(files: [FooBulkUploadFileInput!]!)`
* on bucket tables) reference custom Input types in their args. These are inlined
* into the generated `*Select` types via `typeRefToTs`, but were never registered
* for generation, leaving the referenced Input interfaces undefined. Seed them
* here so `generateCustomInputTypes` emits them (transitively, via its nested
* field-type follow logic).
*/
function collectFieldArgInputTypes(tables: Table[]): Set<string> {
const out = new Set<string>();
for (const table of tables) {
for (const field of table.fields) {
if (!field.args || field.args.length === 0) continue;
for (const arg of field.args) {
const baseName = getTypeBaseName(arg.type);
if (!baseName) continue;
if (baseName.endsWith('Input') || baseName.endsWith('Filter')) {
out.add(baseName);
}
}
}
}
return out;
}

// ============================================================================
// Main Generator (AST-based)
// ============================================================================
Expand Down Expand Up @@ -2066,6 +2093,15 @@ export function generateInputTypesFile(
for (const typeName of filterExtraTypes) {
mergedUsedInputTypes.add(typeName);
}
// Seed input types referenced by table-field args (e.g.,
// `requestBulkUploadUrls(files: [DataRoomBucketsBulkUploadFileInput!]!)`
// on bucket tables). These are inlined into *Select types via typeRefToTs
// but are not part of any operation's args, so they would otherwise never
// be registered for generation.
const fieldArgTypes = collectFieldArgInputTypes(tablesList);
for (const typeName of fieldArgTypes) {
mergedUsedInputTypes.add(typeName);
}
}
const tableCrudTypes = tables ? buildTableCrudTypeNames(tables) : undefined;
// Pass customScalarTypes + enumTypes as already-generated to avoid duplicate declarations
Expand Down
Loading