@constructive-io/graphql-codegen 3.3.1 → 4.0.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.
Files changed (67) hide show
  1. package/README.md +0 -4
  2. package/core/ast.js +6 -5
  3. package/core/codegen/custom-mutations.js +22 -22
  4. package/core/codegen/custom-queries.js +24 -17
  5. package/core/codegen/hooks-ast.d.ts +1 -1
  6. package/core/codegen/hooks-ast.js +22 -5
  7. package/core/codegen/index.d.ts +1 -1
  8. package/core/codegen/mutations.js +16 -12
  9. package/core/codegen/orm/custom-ops-generator.js +37 -3
  10. package/core/codegen/orm/input-types-generator.js +161 -89
  11. package/core/codegen/orm/model-generator.js +72 -73
  12. package/core/codegen/orm/select-types.d.ts +27 -17
  13. package/core/codegen/queries.js +37 -25
  14. package/core/codegen/schema-types-generator.js +21 -0
  15. package/core/codegen/templates/hooks-selection.ts +12 -0
  16. package/core/codegen/templates/query-builder.ts +103 -59
  17. package/core/codegen/templates/select-types.ts +59 -33
  18. package/core/codegen/types.js +26 -0
  19. package/core/codegen/utils.d.ts +1 -1
  20. package/core/codegen/utils.js +1 -1
  21. package/core/custom-ast.js +9 -8
  22. package/core/database/index.js +2 -3
  23. package/core/index.d.ts +2 -0
  24. package/core/index.js +2 -0
  25. package/core/introspect/infer-tables.js +144 -58
  26. package/core/introspect/transform-schema.d.ts +1 -1
  27. package/core/introspect/transform-schema.js +3 -1
  28. package/esm/core/ast.js +6 -5
  29. package/esm/core/codegen/custom-mutations.js +23 -23
  30. package/esm/core/codegen/custom-queries.js +25 -18
  31. package/esm/core/codegen/hooks-ast.d.ts +1 -1
  32. package/esm/core/codegen/hooks-ast.js +22 -5
  33. package/esm/core/codegen/index.d.ts +1 -1
  34. package/esm/core/codegen/mutations.js +16 -12
  35. package/esm/core/codegen/orm/custom-ops-generator.js +38 -4
  36. package/esm/core/codegen/orm/input-types-generator.js +163 -91
  37. package/esm/core/codegen/orm/model-generator.js +73 -74
  38. package/esm/core/codegen/orm/select-types.d.ts +27 -17
  39. package/esm/core/codegen/queries.js +37 -25
  40. package/esm/core/codegen/schema-types-generator.js +21 -0
  41. package/esm/core/codegen/types.js +26 -0
  42. package/esm/core/codegen/utils.d.ts +1 -1
  43. package/esm/core/codegen/utils.js +1 -1
  44. package/esm/core/custom-ast.js +9 -8
  45. package/esm/core/database/index.js +2 -3
  46. package/esm/core/index.d.ts +2 -0
  47. package/esm/core/index.js +2 -0
  48. package/esm/core/introspect/infer-tables.js +144 -58
  49. package/esm/core/introspect/transform-schema.d.ts +1 -1
  50. package/esm/core/introspect/transform-schema.js +3 -1
  51. package/esm/generators/field-selector.js +1 -0
  52. package/esm/generators/index.d.ts +3 -0
  53. package/esm/generators/index.js +3 -0
  54. package/esm/generators/mutations.js +4 -4
  55. package/esm/generators/select.js +4 -4
  56. package/esm/index.d.ts +1 -1
  57. package/esm/index.js +1 -1
  58. package/esm/types/schema.d.ts +5 -3
  59. package/generators/field-selector.js +1 -0
  60. package/generators/index.d.ts +3 -0
  61. package/generators/index.js +3 -0
  62. package/generators/mutations.js +3 -3
  63. package/generators/select.js +3 -3
  64. package/index.d.ts +1 -1
  65. package/index.js +1 -1
  66. package/package.json +11 -11
  67. package/types/schema.d.ts +5 -3
@@ -14,7 +14,7 @@
14
14
  * ...
15
15
  */
16
16
  import * as t from '@babel/types';
17
- import { buildSelectionArgsCall, callExpr, constDecl, createFunctionParam, createImportDeclaration, createSTypeParam, createTypeReExport, customSelectResultTypeLiteral, destructureParamsWithSelection, exportDeclareFunction, exportFunction, generateHookFileCode, getClientCustomCallUnwrap, objectProp, omitType, returnUseMutation, selectionConfigType, spreadObj, sRef, typeRef, useMutationOptionsType, useMutationResultType, voidStatement, } from './hooks-ast';
17
+ import { buildSelectionArgsCall, callExpr, constDecl, createFunctionParam, createImportDeclaration, createSTypeParam, createTypeReExport, customSelectResultTypeLiteral, destructureParamsWithSelection, exportFunction, generateHookFileCode, getClientCustomCallUnwrap, objectProp, omitType, returnUseMutation, spreadObj, sRef, typeRef, typeRefToTsTypeAST, useMutationOptionsType, useMutationResultType, voidStatement, } from './hooks-ast';
18
18
  import { getSelectTypeName } from './select-helpers';
19
19
  import { createTypeTracker, getOperationFileName, getOperationHookName, getTypeBaseName, typeRefToTsType, } from './type-resolver';
20
20
  import { ucFirst } from './utils';
@@ -75,7 +75,7 @@ function generateCustomMutationHookInternal(options) {
75
75
  statements.push(createImportDeclaration('../../orm/input-types', inputTypeImports, true));
76
76
  }
77
77
  if (hasSelect) {
78
- statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'StrictSelect'], true));
78
+ statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'HookStrictSelect', 'StrictSelect'], true));
79
79
  }
80
80
  // Re-exports
81
81
  if (hasArgs) {
@@ -90,39 +90,38 @@ function generateCustomMutationHookInternal(options) {
90
90
  ? typeRef(varTypeName)
91
91
  : t.tsVoidKeyword();
92
92
  const selectedResultType = (sel) => customSelectResultTypeLiteral(operation.name, operation.returnType, payloadTypeName, sel);
93
- // Overload 1: with selection.fields
94
- const o1ParamType = t.tsIntersectionType([
93
+ const paramsType = t.tsIntersectionType([
95
94
  t.tsTypeLiteral([
96
95
  t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(t.tsParenthesizedType(t.tsIntersectionType([
97
96
  t.tsTypeLiteral([
98
- t.tsPropertySignature(t.identifier('fields'), t.tsTypeAnnotation(sRef())),
97
+ t.tsPropertySignature(t.identifier('fields'), t.tsTypeAnnotation(t.tsIntersectionType([sRef(), typeRef(selectTypeName)]))),
98
+ ]),
99
+ typeRef('HookStrictSelect', [
100
+ typeRef('NoInfer', [sRef()]),
101
+ typeRef(selectTypeName),
99
102
  ]),
100
- typeRef('StrictSelect', [sRef(), typeRef(selectTypeName)]),
101
103
  ])))),
102
104
  ]),
103
105
  useMutationOptionsType(selectedResultType(sRef()), mutationVarType),
104
106
  ]);
105
- statements.push(exportDeclareFunction(hookName, createSTypeParam(selectTypeName), [createFunctionParam('params', o1ParamType)], useMutationResultType(selectedResultType(sRef()), mutationVarType)));
106
- // Implementation
107
- const implSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))));
108
- const implParamType = t.tsIntersectionType([
109
- t.tsTypeLiteral([implSelProp]),
110
- omitType(typeRef('UseMutationOptions', [
111
- t.tsAnyKeyword(),
112
- typeRef('Error'),
113
- mutationVarType,
114
- ]), ['mutationFn']),
115
- ]);
116
107
  const body = [];
117
- body.push(buildSelectionArgsCall(selectTypeName));
108
+ body.push(buildSelectionArgsCall(sRef()));
118
109
  body.push(destructureParamsWithSelection('mutationOptions'));
119
110
  body.push(voidStatement('_selection'));
120
111
  const mutationKeyExpr = useCentralizedKeys
121
112
  ? callExpr(t.memberExpression(t.identifier('customMutationKeys'), t.identifier(operation.name)), [])
122
113
  : undefined;
123
- const selectArgExpr = t.objectExpression([
114
+ // Cast to ORM's StrictSelect (not HookStrictSelect) since the ORM
115
+ // method signature requires { select: S } & StrictSelect<S, XxxSelect>.
116
+ // Strictness is already enforced at the hook parameter level.
117
+ const selectArgExpr = t.tsAsExpression(t.objectExpression([
124
118
  objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select'))),
125
- ]);
119
+ ]), t.tsIntersectionType([
120
+ t.tsTypeLiteral([
121
+ t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(sRef())),
122
+ ]),
123
+ typeRef('StrictSelect', [sRef(), typeRef(selectTypeName)]),
124
+ ]));
126
125
  let mutationFnExpr;
127
126
  if (hasArgs) {
128
127
  const variablesParam = createFunctionParam('variables', typeRef(varTypeName));
@@ -132,13 +131,14 @@ function generateCustomMutationHookInternal(options) {
132
131
  mutationFnExpr = t.arrowFunctionExpression([], getClientCustomCallUnwrap('mutation', operation.name, [], selectArgExpr));
133
132
  }
134
133
  body.push(returnUseMutation(mutationFnExpr, [spreadObj(t.identifier('mutationOptions'))], mutationKeyExpr));
135
- statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType)], body));
134
+ statements.push(exportFunction(hookName, createSTypeParam(selectTypeName), [createFunctionParam('params', paramsType)], body, useMutationResultType(selectedResultType(sRef()), mutationVarType)));
136
135
  }
137
136
  else {
138
137
  // Without select: simple hook (scalar return type)
139
- const resultTypeStr = typeRefToTsType(operation.returnType, tracker);
138
+ typeRefToTsType(operation.returnType, tracker);
139
+ const resultTsType = typeRefToTsTypeAST(operation.returnType);
140
140
  const resultTypeLiteral = t.tsTypeLiteral([
141
- t.tsPropertySignature(t.identifier(operation.name), t.tsTypeAnnotation(typeRef(resultTypeStr))),
141
+ t.tsPropertySignature(t.identifier(operation.name), t.tsTypeAnnotation(resultTsType)),
142
142
  ]);
143
143
  const mutationVarType = hasArgs
144
144
  ? typeRef(varTypeName)
@@ -15,7 +15,7 @@
15
15
  */
16
16
  import * as t from '@babel/types';
17
17
  import { asConst } from './babel-ast';
18
- import { addJSDocComment, buildSelectionArgsCall, callExpr, constDecl, createFunctionParam, createImportDeclaration, createSAndTDataTypeParams, createSTypeParam, createTDataTypeParam, createTypeReExport, customSelectResultTypeLiteral, destructureParamsWithSelection, exportAsyncDeclareFunction, exportAsyncFunction, exportDeclareFunction, exportFunction, generateHookFileCode, getClientCustomCallUnwrap, objectProp, omitType, returnUseQuery, selectionConfigType, spreadObj, sRef, typeRef, useQueryOptionsImplType, voidStatement, } from './hooks-ast';
18
+ import { addJSDocComment, buildSelectionArgsCall, callExpr, constDecl, createFunctionParam, createImportDeclaration, createSAndTDataTypeParams, createSTypeParam, createTDataTypeParam, createTypeReExport, customSelectResultTypeLiteral, destructureParamsWithSelection, exportAsyncDeclareFunction, exportAsyncFunction, exportDeclareFunction, exportFunction, generateHookFileCode, getClientCustomCallUnwrap, objectProp, omitType, returnUseQuery, selectionConfigType, spreadObj, sRef, typeRef, typeRefToTsTypeAST, useQueryOptionsImplType, voidStatement, } from './hooks-ast';
19
19
  import { getSelectTypeName } from './select-helpers';
20
20
  import { createTypeTracker, getOperationFileName, getOperationHookName, getQueryKeyName, getTypeBaseName, isTypeRequired, typeRefToTsType, } from './type-resolver';
21
21
  import { ucFirst } from './utils';
@@ -28,10 +28,11 @@ export function generateCustomQueryHook(options) {
28
28
  const tracker = createTypeTracker({ tableTypeNames });
29
29
  const hasArgs = operation.args.length > 0;
30
30
  const hasRequiredArgs = operation.args.some((arg) => isTypeRequired(arg.type));
31
- const resultType = typeRefToTsType(operation.returnType, tracker);
31
+ typeRefToTsType(operation.returnType, tracker);
32
32
  for (const arg of operation.args) {
33
33
  typeRefToTsType(arg.type, tracker);
34
34
  }
35
+ const resultTsType = typeRefToTsTypeAST(operation.returnType);
35
36
  const selectTypeName = getSelectTypeName(operation.returnType);
36
37
  const payloadTypeName = getTypeBaseName(operation.returnType);
37
38
  const hasSelect = !!selectTypeName && !!payloadTypeName;
@@ -69,7 +70,7 @@ export function generateCustomQueryHook(options) {
69
70
  statements.push(createImportDeclaration('../../orm/input-types', inputTypeImports, true));
70
71
  }
71
72
  if (hasSelect) {
72
- statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'StrictSelect'], true));
73
+ statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'HookStrictSelect'], true));
73
74
  }
74
75
  // Re-exports
75
76
  if (hasArgs) {
@@ -121,9 +122,12 @@ export function generateCustomQueryHook(options) {
121
122
  // Helper to build the fields+StrictSelect intersection type
122
123
  const fieldsSelectionType = (s) => t.tsParenthesizedType(t.tsIntersectionType([
123
124
  t.tsTypeLiteral([
124
- t.tsPropertySignature(t.identifier('fields'), t.tsTypeAnnotation(s)),
125
+ t.tsPropertySignature(t.identifier('fields'), t.tsTypeAnnotation(t.tsIntersectionType([s, typeRef(selectTypeName)]))),
126
+ ]),
127
+ typeRef('HookStrictSelect', [
128
+ typeRef('NoInfer', [s]),
129
+ typeRef(selectTypeName),
125
130
  ]),
126
- typeRef('StrictSelect', [s, typeRef(selectTypeName)]),
127
131
  ]));
128
132
  const selectedResultType = (sel) => customSelectResultTypeLiteral(operation.name, operation.returnType, payloadTypeName, sel);
129
133
  // Hook
@@ -141,10 +145,11 @@ export function generateCustomQueryHook(options) {
141
145
  // Overload 1: with selection.fields
142
146
  const o1Props = [];
143
147
  if (hasArgs) {
144
- const varType = hasRequiredArgs
145
- ? typeRef(varTypeName)
146
- : t.tsUnionType([typeRef(varTypeName), t.tsUndefinedKeyword()]);
147
- o1Props.push(t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(varType)));
148
+ const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName)));
149
+ if (!hasRequiredArgs) {
150
+ varProp.optional = true;
151
+ }
152
+ o1Props.push(varProp);
148
153
  }
149
154
  o1Props.push(t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(fieldsSelectionType(sRef()))));
150
155
  const o1ParamType = t.tsIntersectionType([
@@ -230,7 +235,7 @@ export function generateCustomQueryHook(options) {
230
235
  else {
231
236
  // Without select: simple hook (scalar return type)
232
237
  const resultTypeLiteral = t.tsTypeLiteral([
233
- t.tsPropertySignature(t.identifier(operation.name), t.tsTypeAnnotation(typeRef(resultType))),
238
+ t.tsPropertySignature(t.identifier(operation.name), t.tsTypeAnnotation(resultTsType)),
234
239
  ]);
235
240
  const optType = omitType(typeRef('UseQueryOptions', [
236
241
  resultTypeLiteral,
@@ -309,10 +314,11 @@ export function generateCustomQueryHook(options) {
309
314
  // Overload 1: with fields
310
315
  const f1Props = [];
311
316
  if (hasArgs) {
312
- const varType = hasRequiredArgs
313
- ? typeRef(varTypeName)
314
- : t.tsUnionType([typeRef(varTypeName), t.tsUndefinedKeyword()]);
315
- f1Props.push(t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(varType)));
317
+ const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName)));
318
+ if (!hasRequiredArgs) {
319
+ varProp.optional = true;
320
+ }
321
+ f1Props.push(varProp);
316
322
  }
317
323
  f1Props.push(t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(fieldsSelectionType(sRef()))));
318
324
  const f1Decl = exportAsyncDeclareFunction(fetchFnName, createSTypeParam(selectTypeName), [createFunctionParam('params', t.tsTypeLiteral(f1Props))], typeRef('Promise', [selectedResultType(sRef())]));
@@ -415,10 +421,11 @@ export function generateCustomQueryHook(options) {
415
421
  // Overload 1: with fields
416
422
  const p1Props = [];
417
423
  if (hasArgs) {
418
- const varType = hasRequiredArgs
419
- ? typeRef(varTypeName)
420
- : t.tsUnionType([typeRef(varTypeName), t.tsUndefinedKeyword()]);
421
- p1Props.push(t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(varType)));
424
+ const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName)));
425
+ if (!hasRequiredArgs) {
426
+ varProp.optional = true;
427
+ }
428
+ p1Props.push(varProp);
422
429
  }
423
430
  p1Props.push(t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(fieldsSelectionType(sRef()))));
424
431
  const p1Decl = exportAsyncDeclareFunction(prefetchFnName, createSTypeParam(selectTypeName), [
@@ -70,6 +70,6 @@ export declare function generateHookFileCode(headerDescription: string, statemen
70
70
  export declare function scopeTypeLiteral(scopeTypeName: string): t.TSTypeLiteral;
71
71
  export declare function wrapInferSelectResultType(typeRefNode: CleanArgument['type'], payloadTypeName: string, selectType: t.TSType): t.TSType;
72
72
  export declare function typeRefToTsTypeAST(typeRefNode: CleanArgument['type']): t.TSType;
73
- export declare function buildSelectionArgsCall(selectTypeName: string): t.VariableDeclaration;
73
+ export declare function buildSelectionArgsCall(selectType: string | t.TSType): t.VariableDeclaration;
74
74
  export declare function buildListSelectionArgsCall(selectTypeName: string, filterTypeName: string, orderByTypeName: string): t.VariableDeclaration;
75
75
  export declare function customSelectResultTypeLiteral(opName: string, returnType: CleanArgument['type'], payloadTypeName: string, selectType: t.TSType): t.TSTypeLiteral;
@@ -96,7 +96,10 @@ export function selectionConfigType(selectType) {
96
96
  return typeRef('SelectionConfig', [selectType]);
97
97
  }
98
98
  export function strictSelectType(selectType, shapeTypeName) {
99
- return typeRef('StrictSelect', [selectType, typeRef(shapeTypeName)]);
99
+ return typeRef('HookStrictSelect', [
100
+ typeRef('NoInfer', [selectType]),
101
+ typeRef(shapeTypeName),
102
+ ]);
100
103
  }
101
104
  export function withFieldsSelectionType(selectType, selectTypeName) {
102
105
  return t.tsIntersectionType([
@@ -365,8 +368,15 @@ export function scopeTypeLiteral(scopeTypeName) {
365
368
  // Type conversion helpers (GraphQL -> AST)
366
369
  // ============================================================================
367
370
  export function wrapInferSelectResultType(typeRefNode, payloadTypeName, selectType) {
371
+ const nonNullable = wrapInferSelectResultTypeNonNullable(typeRefNode, payloadTypeName, selectType);
372
+ if (typeRefNode.kind === 'NON_NULL') {
373
+ return nonNullable;
374
+ }
375
+ return t.tsUnionType([nonNullable, t.tsNullKeyword()]);
376
+ }
377
+ function wrapInferSelectResultTypeNonNullable(typeRefNode, payloadTypeName, selectType) {
368
378
  if (typeRefNode.kind === 'NON_NULL' && typeRefNode.ofType) {
369
- return wrapInferSelectResultType(typeRefNode.ofType, payloadTypeName, selectType);
379
+ return wrapInferSelectResultTypeNonNullable(typeRefNode.ofType, payloadTypeName, selectType);
370
380
  }
371
381
  if (typeRefNode.kind === 'LIST' && typeRefNode.ofType) {
372
382
  return t.tsArrayType(wrapInferSelectResultType(typeRefNode.ofType, payloadTypeName, selectType));
@@ -374,8 +384,15 @@ export function wrapInferSelectResultType(typeRefNode, payloadTypeName, selectTy
374
384
  return inferSelectResultType(payloadTypeName, selectType);
375
385
  }
376
386
  export function typeRefToTsTypeAST(typeRefNode) {
387
+ const nonNullable = typeRefToTsTypeASTNonNullable(typeRefNode);
388
+ if (typeRefNode.kind === 'NON_NULL') {
389
+ return nonNullable;
390
+ }
391
+ return t.tsUnionType([nonNullable, t.tsNullKeyword()]);
392
+ }
393
+ function typeRefToTsTypeASTNonNullable(typeRefNode) {
377
394
  if (typeRefNode.kind === 'NON_NULL' && typeRefNode.ofType) {
378
- return typeRefToTsTypeAST(typeRefNode.ofType);
395
+ return typeRefToTsTypeASTNonNullable(typeRefNode.ofType);
379
396
  }
380
397
  if (typeRefNode.kind === 'LIST' && typeRefNode.ofType) {
381
398
  return t.tsArrayType(typeRefToTsTypeAST(typeRefNode.ofType));
@@ -392,13 +409,13 @@ export function typeRefToTsTypeAST(typeRefNode) {
392
409
  }
393
410
  return typeRef(typeRefNode.name ?? 'unknown');
394
411
  }
395
- export function buildSelectionArgsCall(selectTypeName) {
412
+ export function buildSelectionArgsCall(selectType) {
396
413
  const call = t.callExpression(t.identifier('buildSelectionArgs'), [
397
414
  t.memberExpression(t.identifier('params'), t.identifier('selection')),
398
415
  ]);
399
416
  // @ts-ignore - Babel types support typeParameters on CallExpression for TS
400
417
  call.typeParameters = t.tsTypeParameterInstantiation([
401
- typeRef(selectTypeName),
418
+ typeof selectType === 'string' ? typeRef(selectType) : selectType,
402
419
  ]);
403
420
  return constDecl('args', call);
404
421
  }
@@ -42,7 +42,7 @@ export interface GenerateResult {
42
42
  };
43
43
  }
44
44
  export interface GenerateOptions {
45
- /** Tables from _meta introspection */
45
+ /** Tables from GraphQL introspection */
46
46
  tables: CleanTable[];
47
47
  /** Custom operations from __schema introspection */
48
48
  customOperations?: {
@@ -26,9 +26,12 @@ function buildMutationResultType(mutationName, singularName, relationTypeName, s
26
26
  function buildFieldsSelectionType(s, selectTypeName) {
27
27
  return t.tsParenthesizedType(t.tsIntersectionType([
28
28
  t.tsTypeLiteral([
29
- t.tsPropertySignature(t.identifier('fields'), t.tsTypeAnnotation(s)),
29
+ t.tsPropertySignature(t.identifier('fields'), t.tsTypeAnnotation(t.tsIntersectionType([s, typeRef(selectTypeName)]))),
30
+ ]),
31
+ typeRef('HookStrictSelect', [
32
+ typeRef('NoInfer', [s]),
33
+ typeRef(selectTypeName),
30
34
  ]),
31
- typeRef('StrictSelect', [s, typeRef(selectTypeName)]),
32
35
  ]));
33
36
  }
34
37
  export function generateCreateMutationHook(table, options = {}) {
@@ -58,7 +61,7 @@ export function generateCreateMutationHook(table, options = {}) {
58
61
  statements.push(createImportDeclaration('../mutation-keys', [mutationKeysName]));
59
62
  }
60
63
  statements.push(createImportDeclaration('../../orm/input-types', [selectTypeName, relationTypeName, createInputTypeName], true));
61
- statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'StrictSelect'], true));
64
+ statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'HookStrictSelect'], true));
62
65
  // Re-exports
63
66
  statements.push(createTypeReExport([selectTypeName, relationTypeName, createInputTypeName], '../../orm/input-types'));
64
67
  // Variable type: CreateTypeName['singularName']
@@ -147,6 +150,7 @@ export function generateUpdateMutationHook(table, options = {}) {
147
150
  const patchTypeName = `${typeName}Patch`;
148
151
  const pkFields = getPrimaryKeyInfo(table);
149
152
  const pkField = pkFields[0];
153
+ const patchFieldName = table.query?.patchFieldName ?? lcFirst(typeName) + 'Patch';
150
154
  const pkTsType = pkField.tsType === 'string' ? t.tsStringKeyword() : t.tsNumberKeyword();
151
155
  const statements = [];
152
156
  // Imports
@@ -163,13 +167,13 @@ export function generateUpdateMutationHook(table, options = {}) {
163
167
  statements.push(createImportDeclaration('../mutation-keys', [mutationKeysName]));
164
168
  }
165
169
  statements.push(createImportDeclaration('../../orm/input-types', [selectTypeName, relationTypeName, patchTypeName], true));
166
- statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'StrictSelect'], true));
170
+ statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'HookStrictSelect'], true));
167
171
  // Re-exports
168
172
  statements.push(createTypeReExport([selectTypeName, relationTypeName, patchTypeName], '../../orm/input-types'));
169
- // Variable type: { pkField: type; patch: PatchType }
173
+ // Variable type: { pkField: type; patchFieldName: PatchType }
170
174
  const updateVarType = t.tsTypeLiteral([
171
175
  t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTsType)),
172
- t.tsPropertySignature(t.identifier('patch'), t.tsTypeAnnotation(typeRef(patchTypeName))),
176
+ t.tsPropertySignature(t.identifier(patchFieldName), t.tsTypeAnnotation(typeRef(patchTypeName))),
173
177
  ]);
174
178
  const resultType = (sel) => buildMutationResultType(mutationName, singularName, relationTypeName, sel);
175
179
  // Overload 1: with fields
@@ -189,7 +193,7 @@ export function generateUpdateMutationHook(table, options = {}) {
189
193
  ' selection: { fields: { id: true, name: true } },',
190
194
  '});',
191
195
  '',
192
- `mutate({ ${pkField.name}: 'value-here', patch: { name: 'Updated' } });`,
196
+ `mutate({ ${pkField.name}: 'value-here', ${patchFieldName}: { name: 'Updated' } });`,
193
197
  '```',
194
198
  ]);
195
199
  statements.push(o1);
@@ -211,16 +215,16 @@ export function generateUpdateMutationHook(table, options = {}) {
211
215
  const mutationKeyExpr = useCentralizedKeys
212
216
  ? t.memberExpression(t.identifier(mutationKeysName), t.identifier('all'))
213
217
  : undefined;
214
- // mutationFn: ({ pkField, patch }: { pkField: type; patch: PatchType }) =>
215
- // getClient().singular.update({ where: { pkField }, data: patch, select: ... }).unwrap()
218
+ // mutationFn: ({ pkField, patchFieldName }: VarType) =>
219
+ // getClient().singular.update({ where: { pkField }, data: patchFieldName, select: ... }).unwrap()
216
220
  const destructParam = t.objectPattern([
217
221
  shorthandProp(pkField.name),
218
- shorthandProp('patch'),
222
+ shorthandProp(patchFieldName),
219
223
  ]);
220
224
  destructParam.typeAnnotation = t.tsTypeAnnotation(updateVarType);
221
225
  const mutationFnExpr = t.arrowFunctionExpression([destructParam], getClientCallUnwrap(singularName, 'update', t.objectExpression([
222
226
  objectProp('where', t.objectExpression([shorthandProp(pkField.name)])),
223
- objectProp('data', t.identifier('patch')),
227
+ objectProp('data', t.identifier(patchFieldName)),
224
228
  objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select'))),
225
229
  ])));
226
230
  // onSuccess: invalidate detail and lists
@@ -288,7 +292,7 @@ export function generateDeleteMutationHook(table, options = {}) {
288
292
  statements.push(createImportDeclaration('../mutation-keys', [mutationKeysName]));
289
293
  }
290
294
  statements.push(createImportDeclaration('../../orm/input-types', [selectTypeName, relationTypeName], true));
291
- statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'StrictSelect'], true));
295
+ statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'HookStrictSelect'], true));
292
296
  // Re-exports
293
297
  statements.push(createTypeReExport([selectTypeName, relationTypeName], '../../orm/input-types'));
294
298
  // Variable type: { pkField: type }
@@ -7,7 +7,7 @@
7
7
  import * as t from '@babel/types';
8
8
  import { generateCode } from '../babel-ast';
9
9
  import { NON_SELECT_TYPES, getSelectTypeName } from '../select-helpers';
10
- import { getTypeBaseName, isTypeRequired, typeRefToTsType, } from '../type-resolver';
10
+ import { getTypeBaseName, isTypeRequired, scalarToTsType, typeRefToTsType, } from '../type-resolver';
11
11
  import { getGeneratedFileHeader, ucFirst } from '../utils';
12
12
  /**
13
13
  * Collect all input type names used by operations
@@ -105,8 +105,15 @@ function parseTypeAnnotation(typeStr) {
105
105
  return t.tsTypeReference(t.identifier(typeStr));
106
106
  }
107
107
  function buildSelectedResultTsType(typeRef, payloadTypeName) {
108
+ const nonNullable = buildSelectedResultTsTypeNonNullable(typeRef, payloadTypeName);
109
+ if (typeRef.kind === 'NON_NULL') {
110
+ return nonNullable;
111
+ }
112
+ return t.tsUnionType([nonNullable, t.tsNullKeyword()]);
113
+ }
114
+ function buildSelectedResultTsTypeNonNullable(typeRef, payloadTypeName) {
108
115
  if (typeRef.kind === 'NON_NULL' && typeRef.ofType) {
109
- return buildSelectedResultTsType(typeRef.ofType, payloadTypeName);
116
+ return buildSelectedResultTsTypeNonNullable(typeRef.ofType, payloadTypeName);
110
117
  }
111
118
  if (typeRef.kind === 'LIST' && typeRef.ofType) {
112
119
  return t.tsArrayType(buildSelectedResultTsType(typeRef.ofType, payloadTypeName));
@@ -116,6 +123,34 @@ function buildSelectedResultTsType(typeRef, payloadTypeName) {
116
123
  t.tsTypeReference(t.identifier('S')),
117
124
  ]));
118
125
  }
126
+ function scalarTsTypeToAst(typeName) {
127
+ if (typeName === 'string')
128
+ return t.tsStringKeyword();
129
+ if (typeName === 'number')
130
+ return t.tsNumberKeyword();
131
+ if (typeName === 'boolean')
132
+ return t.tsBooleanKeyword();
133
+ return t.tsUnknownKeyword();
134
+ }
135
+ function buildRawResultTsType(typeRef) {
136
+ const nonNullable = buildRawResultTsTypeNonNullable(typeRef);
137
+ if (typeRef.kind === 'NON_NULL') {
138
+ return nonNullable;
139
+ }
140
+ return t.tsUnionType([nonNullable, t.tsNullKeyword()]);
141
+ }
142
+ function buildRawResultTsTypeNonNullable(typeRef) {
143
+ if (typeRef.kind === 'NON_NULL' && typeRef.ofType) {
144
+ return buildRawResultTsTypeNonNullable(typeRef.ofType);
145
+ }
146
+ if (typeRef.kind === 'LIST' && typeRef.ofType) {
147
+ return t.tsArrayType(buildRawResultTsType(typeRef.ofType));
148
+ }
149
+ if (typeRef.kind === 'SCALAR') {
150
+ return scalarTsTypeToAst(scalarToTsType(typeRef.name ?? 'unknown'));
151
+ }
152
+ return t.tsTypeReference(t.identifier(typeRef.name ?? 'unknown'));
153
+ }
119
154
  function buildOperationMethod(op, operationType) {
120
155
  const hasArgs = op.args.length > 0;
121
156
  const varTypeName = `${ucFirst(op.name)}Variables`;
@@ -198,10 +233,9 @@ function buildOperationMethod(op, operationType) {
198
233
  }
199
234
  else {
200
235
  // Scalar/Connection type: use raw TS type directly
201
- const rawTsType = typeRefToTsType(op.returnType);
202
236
  newExpr.typeParameters = t.tsTypeParameterInstantiation([
203
237
  t.tsTypeLiteral([
204
- t.tsPropertySignature(t.identifier(op.name), t.tsTypeAnnotation(parseTypeAnnotation(rawTsType))),
238
+ t.tsPropertySignature(t.identifier(op.name), t.tsTypeAnnotation(buildRawResultTsType(op.returnType))),
205
239
  ]),
206
240
  ]);
207
241
  }