@constructive-io/graphql-codegen 4.40.5 → 4.41.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.
@@ -275,7 +275,7 @@ function buildFindManyArgsType(table) {
275
275
  }
276
276
  /**
277
277
  * Build the FindFirstArgs type instantiation for a table:
278
- * FindFirstArgs<SelectType, FilterType> & { select: SelectType }
278
+ * FindFirstArgs<SelectType, FilterType, OrderByType> & { select: SelectType }
279
279
  *
280
280
  * The intersection with { select: SelectType } makes select required,
281
281
  * matching what the ORM's findFirst method expects.
@@ -284,9 +284,11 @@ function buildFindFirstArgsType(table) {
284
284
  const { typeName } = getTableNames(table);
285
285
  const selectTypeName = `${typeName}Select`;
286
286
  const whereTypeName = getFilterTypeName(table);
287
+ const orderByTypeName = getOrderByTypeName(table);
287
288
  const findFirstType = t.tsTypeReference(t.identifier('FindFirstArgs'), t.tsTypeParameterInstantiation([
288
289
  t.tsTypeReference(t.identifier(selectTypeName)),
289
290
  t.tsTypeReference(t.identifier(whereTypeName)),
291
+ t.tsTypeReference(t.identifier(orderByTypeName)),
290
292
  ]));
291
293
  // Intersect with { select: SelectType } to make select required
292
294
  return t.tsIntersectionType([
@@ -679,6 +681,114 @@ function buildMutationHandler(table, operation, vectorFieldNames, targetName, ty
679
681
  t.tryStatement(t.blockStatement(tryBody), buildErrorCatch(`Failed to ${operation} record.`)),
680
682
  ]), false, true);
681
683
  }
684
+ function buildBulkMutationHandler(table, operation, targetName) {
685
+ const { singularName } = getTableNames(table);
686
+ const selectObj = buildSelectObject(table);
687
+ // Map CLI op name to ORM method name
688
+ const ormMethod = (() => {
689
+ switch (operation) {
690
+ case 'bulk-create': return 'bulkCreate';
691
+ case 'bulk-upsert': return 'bulkUpsert';
692
+ case 'bulk-update': return 'bulkUpdate';
693
+ case 'bulk-delete': return 'bulkDelete';
694
+ }
695
+ })();
696
+ const tryBody = [];
697
+ if (operation === 'bulk-create' || operation === 'bulk-upsert') {
698
+ // Parse --data (JSON array) from argv
699
+ tryBody.push(t.variableDeclaration('const', [
700
+ t.variableDeclarator(t.identifier('dataRaw'), t.memberExpression(t.identifier('argv'), t.identifier('data'))),
701
+ ]));
702
+ tryBody.push(t.ifStatement(t.unaryExpression('!', t.identifier('dataRaw')), t.blockStatement([
703
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('error')), [t.stringLiteral(`--data is required for ${operation}. Provide a JSON array.`)])),
704
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('process'), t.identifier('exit')), [t.numericLiteral(1)])),
705
+ ])));
706
+ tryBody.push(t.variableDeclaration('const', [
707
+ t.variableDeclarator(t.identifier('data'), t.callExpression(t.memberExpression(t.identifier('JSON'), t.identifier('parse')), [t.tsAsExpression(t.identifier('dataRaw'), t.tsStringKeyword())])),
708
+ ]));
709
+ let ormArgs;
710
+ if (operation === 'bulk-upsert') {
711
+ // Also parse --on-conflict
712
+ tryBody.push(t.variableDeclaration('const', [
713
+ t.variableDeclarator(t.identifier('onConflictRaw'), t.memberExpression(t.identifier('argv'), t.identifier('on-conflict'))),
714
+ ]));
715
+ tryBody.push(t.variableDeclaration('const', [
716
+ t.variableDeclarator(t.identifier('onConflict'), t.conditionalExpression(t.identifier('onConflictRaw'), t.callExpression(t.memberExpression(t.identifier('JSON'), t.identifier('parse')), [t.tsAsExpression(t.identifier('onConflictRaw'), t.tsStringKeyword())]), t.objectExpression([]))),
717
+ ]));
718
+ ormArgs = t.objectExpression([
719
+ t.objectProperty(t.identifier('data'), t.identifier('data')),
720
+ t.objectProperty(t.identifier('onConflict'), t.identifier('onConflict')),
721
+ t.objectProperty(t.identifier('select'), selectObj),
722
+ ]);
723
+ }
724
+ else {
725
+ ormArgs = t.objectExpression([
726
+ t.objectProperty(t.identifier('data'), t.identifier('data')),
727
+ t.objectProperty(t.identifier('select'), selectObj),
728
+ ]);
729
+ }
730
+ tryBody.push(buildGetClientStatement(targetName));
731
+ tryBody.push(t.variableDeclaration('const', [
732
+ t.variableDeclarator(t.identifier('result'), t.awaitExpression(buildOrmCall(singularName, ormMethod, ormArgs))),
733
+ ]));
734
+ }
735
+ else if (operation === 'bulk-update') {
736
+ // Parse --where (JSON) and --data (JSON)
737
+ tryBody.push(t.variableDeclaration('const', [
738
+ t.variableDeclarator(t.identifier('whereRaw'), t.memberExpression(t.identifier('argv'), t.identifier('where'))),
739
+ ]));
740
+ tryBody.push(t.variableDeclaration('const', [
741
+ t.variableDeclarator(t.identifier('dataRaw'), t.memberExpression(t.identifier('argv'), t.identifier('data'))),
742
+ ]));
743
+ tryBody.push(t.ifStatement(t.logicalExpression('||', t.unaryExpression('!', t.identifier('whereRaw')), t.unaryExpression('!', t.identifier('dataRaw'))), t.blockStatement([
744
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('error')), [t.stringLiteral('--where and --data are required for bulk-update. Provide JSON objects.')])),
745
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('process'), t.identifier('exit')), [t.numericLiteral(1)])),
746
+ ])));
747
+ tryBody.push(t.variableDeclaration('const', [
748
+ t.variableDeclarator(t.identifier('where'), t.callExpression(t.memberExpression(t.identifier('JSON'), t.identifier('parse')), [t.tsAsExpression(t.identifier('whereRaw'), t.tsStringKeyword())])),
749
+ ]));
750
+ tryBody.push(t.variableDeclaration('const', [
751
+ t.variableDeclarator(t.identifier('data'), t.callExpression(t.memberExpression(t.identifier('JSON'), t.identifier('parse')), [t.tsAsExpression(t.identifier('dataRaw'), t.tsStringKeyword())])),
752
+ ]));
753
+ tryBody.push(buildGetClientStatement(targetName));
754
+ tryBody.push(t.variableDeclaration('const', [
755
+ t.variableDeclarator(t.identifier('result'), t.awaitExpression(buildOrmCall(singularName, ormMethod, t.objectExpression([
756
+ t.objectProperty(t.identifier('where'), t.identifier('where')),
757
+ t.objectProperty(t.identifier('data'), t.identifier('data')),
758
+ t.objectProperty(t.identifier('select'), selectObj),
759
+ ])))),
760
+ ]));
761
+ }
762
+ else {
763
+ // bulk-delete: parse --where (JSON)
764
+ tryBody.push(t.variableDeclaration('const', [
765
+ t.variableDeclarator(t.identifier('whereRaw'), t.memberExpression(t.identifier('argv'), t.identifier('where'))),
766
+ ]));
767
+ tryBody.push(t.ifStatement(t.unaryExpression('!', t.identifier('whereRaw')), t.blockStatement([
768
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('error')), [t.stringLiteral('--where is required for bulk-delete. Provide a JSON object.')])),
769
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('process'), t.identifier('exit')), [t.numericLiteral(1)])),
770
+ ])));
771
+ tryBody.push(t.variableDeclaration('const', [
772
+ t.variableDeclarator(t.identifier('where'), t.callExpression(t.memberExpression(t.identifier('JSON'), t.identifier('parse')), [t.tsAsExpression(t.identifier('whereRaw'), t.tsStringKeyword())])),
773
+ ]));
774
+ tryBody.push(buildGetClientStatement(targetName));
775
+ tryBody.push(t.variableDeclaration('const', [
776
+ t.variableDeclarator(t.identifier('result'), t.awaitExpression(buildOrmCall(singularName, ormMethod, t.objectExpression([
777
+ t.objectProperty(t.identifier('where'), t.identifier('where')),
778
+ t.objectProperty(t.identifier('select'), selectObj),
779
+ ])))),
780
+ ]));
781
+ }
782
+ tryBody.push(buildJsonLog(t.identifier('result')));
783
+ const argvParam = t.identifier('argv');
784
+ argvParam.typeAnnotation = buildArgvType();
785
+ const prompterParam = t.identifier('prompter');
786
+ prompterParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Inquirerer')));
787
+ const handlerName = `handle${toPascalCase(operation)}`;
788
+ return t.functionDeclaration(t.identifier(handlerName), [argvParam, prompterParam], t.blockStatement([
789
+ t.tryStatement(t.blockStatement(tryBody), buildErrorCatch(`Failed to ${operation}.`)),
790
+ ]), false, true);
791
+ }
682
792
  export function generateTableCommand(table, options) {
683
793
  const { singularName, typeName } = getTableNames(table);
684
794
  const commandName = toKebabCase(singularName);
@@ -756,6 +866,11 @@ export function generateTableCommand(table, options) {
756
866
  const embedderPath = options?.targetName ? '../../embedder' : '../embedder';
757
867
  statements.push(createImportDeclaration(embedderPath, ['resolveEmbedder', 'autoEmbedWhere', 'autoEmbedInput']));
758
868
  }
869
+ // Detect bulk mutations
870
+ const hasBulkCreate = !!table.query?.bulkInsert;
871
+ const hasBulkUpsert = !!table.query?.bulkUpsert;
872
+ const hasBulkUpdate = !!table.query?.bulkUpdate;
873
+ const hasBulkDelete = !!table.query?.bulkDelete;
759
874
  const subcommands = ['list', 'find-first'];
760
875
  if (hasSearchFields)
761
876
  subcommands.push('search');
@@ -766,6 +881,14 @@ export function generateTableCommand(table, options) {
766
881
  subcommands.push('update');
767
882
  if (hasDelete)
768
883
  subcommands.push('delete');
884
+ if (hasBulkCreate)
885
+ subcommands.push('bulk-create');
886
+ if (hasBulkUpsert)
887
+ subcommands.push('bulk-upsert');
888
+ if (hasBulkUpdate)
889
+ subcommands.push('bulk-update');
890
+ if (hasBulkDelete)
891
+ subcommands.push('bulk-delete');
769
892
  const usageLines = [
770
893
  '',
771
894
  `${commandName} <command>`,
@@ -786,7 +909,15 @@ export function generateTableCommand(table, options) {
786
909
  }
787
910
  if (hasDelete)
788
911
  usageLines.push(` delete Delete a ${singularName}`);
789
- usageLines.push('', 'List Options:', ' --limit <n> Max number of records to return (forward pagination)', ' --last <n> Number of records from the end (backward pagination)', ' --after <cursor> Cursor for forward pagination', ' --before <cursor> Cursor for backward pagination', ' --offset <n> Number of records to skip', ' --select <fields> Comma-separated list of fields to return', ' --where.<field>.<op> Filter (dot-notation, e.g. --where.name.equalTo foo)', ' --condition.<f>.<op> Condition filter (dot-notation)', ' --orderBy <values> Comma-separated ordering values (e.g. NAME_ASC,CREATED_AT_DESC)', '', 'Find-First Options:', ' --select <fields> Comma-separated list of fields to return', ' --where.<field>.<op> Filter (dot-notation, e.g. --where.status.equalTo active)', ' --condition.<f>.<op> Condition filter (dot-notation)', '');
912
+ if (hasBulkCreate)
913
+ usageLines.push(` bulk-create Bulk create ${singularName} records`);
914
+ if (hasBulkUpsert)
915
+ usageLines.push(` bulk-upsert Bulk upsert ${singularName} records`);
916
+ if (hasBulkUpdate)
917
+ usageLines.push(` bulk-update Bulk update ${singularName} records`);
918
+ if (hasBulkDelete)
919
+ usageLines.push(` bulk-delete Bulk delete ${singularName} records`);
920
+ usageLines.push('', 'List Options:', ' --limit <n> Max number of records to return (forward pagination)', ' --last <n> Number of records from the end (backward pagination)', ' --after <cursor> Cursor for forward pagination', ' --before <cursor> Cursor for backward pagination', ' --offset <n> Number of records to skip', ' --select <fields> Comma-separated list of fields to return', ' --where.<field>.<op> Filter (dot-notation, e.g. --where.name.equalTo foo)', ' --condition.<f>.<op> Condition filter (dot-notation)', ' --orderBy <values> Comma-separated ordering values (e.g. NAME_ASC,CREATED_AT_DESC)', '', 'Find-First Options:', ' --select <fields> Comma-separated list of fields to return', ' --where.<field>.<op> Filter (dot-notation, e.g. --where.status.equalTo active)', ' --condition.<f>.<op> Condition filter (dot-notation)', ' --orderBy <values> Comma-separated ordering values (e.g. NAME_ASC,CREATED_AT_DESC)', '');
790
921
  if (hasSearchFields) {
791
922
  usageLines.push('Search Options:', ' <query> Search query string (required)', ' --limit <n> Max number of records to return', ' --offset <n> Number of records to skip', ' --select <fields> Comma-separated list of fields to return', ' --orderBy <values> Comma-separated list of ordering values');
792
923
  if (hasEmbeddings) {
@@ -869,6 +1000,14 @@ export function generateTableCommand(table, options) {
869
1000
  statements.push(buildMutationHandler(table, 'update', vectorFieldNames, tn, options?.typeRegistry, ormTypes));
870
1001
  if (hasDelete)
871
1002
  statements.push(buildMutationHandler(table, 'delete', vectorFieldNames, tn, options?.typeRegistry, ormTypes));
1003
+ if (hasBulkCreate)
1004
+ statements.push(buildBulkMutationHandler(table, 'bulk-create', tn));
1005
+ if (hasBulkUpsert)
1006
+ statements.push(buildBulkMutationHandler(table, 'bulk-upsert', tn));
1007
+ if (hasBulkUpdate)
1008
+ statements.push(buildBulkMutationHandler(table, 'bulk-update', tn));
1009
+ if (hasBulkDelete)
1010
+ statements.push(buildBulkMutationHandler(table, 'bulk-delete', tn));
872
1011
  const header = getGeneratedFileHeader(`CLI commands for ${table.name}`);
873
1012
  const code = generateCode(statements);
874
1013
  return {
@@ -77,6 +77,24 @@ function generateEntityMutationKeysDeclaration(table, relationships) {
77
77
  const deleteProp = t.objectProperty(t.identifier('delete'), deleteArrowFn);
78
78
  addJSDocComment(deleteProp, [`Delete ${singularName} mutation key`]);
79
79
  properties.push(deleteProp);
80
+ // Bulk mutation keys (only if table has bulk operations)
81
+ const bulkOps = [
82
+ { key: 'bulkCreate', queryField: table.query?.bulkInsert },
83
+ { key: 'bulkUpsert', queryField: table.query?.bulkUpsert },
84
+ { key: 'bulkUpdate', queryField: table.query?.bulkUpdate },
85
+ { key: 'bulkDelete', queryField: table.query?.bulkDelete },
86
+ ];
87
+ for (const { key, queryField } of bulkOps) {
88
+ if (!queryField)
89
+ continue;
90
+ const arrowFn = t.arrowFunctionExpression([], constArray([
91
+ t.stringLiteral('mutation'),
92
+ t.stringLiteral(entityKey),
93
+ t.stringLiteral(key),
94
+ ]));
95
+ const prop = t.objectProperty(t.identifier(key), arrowFn);
96
+ properties.push(prop);
97
+ }
80
98
  return t.exportNamedDeclaration(t.variableDeclaration('const', [
81
99
  t.variableDeclarator(t.identifier(keysName), asConst(t.objectExpression(properties))),
82
100
  ]));
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import * as t from '@babel/types';
11
11
  import { addJSDocComment, buildSelectionArgsCall, callExpr, constDecl, createFunctionParam, createImportDeclaration, createSTypeParam, createTypeReExport, destructureParamsWithSelection, exportDeclareFunction, exportFunction, generateHookFileCode, getClientCallUnwrap, inferSelectResultType, objectProp, omitType, returnUseMutation, selectionConfigType, shorthandProp, spreadObj, sRef, typeRef, typeLiteralWithProps, useMutationOptionsType, useMutationResultType, voidStatement, } from './hooks-ast';
12
- import { getCreateMutationFileName, getCreateMutationHookName, getCreateMutationName, getDeleteMutationFileName, getDeleteMutationHookName, getDeleteMutationName, getPrimaryKeyInfo, getTableNames, getUpdateMutationFileName, getUpdateMutationHookName, getUpdateMutationName, hasValidPrimaryKey, lcFirst, } from './utils';
12
+ import { getBulkCreateMutationFileName, getBulkCreateMutationHookName, getBulkDeleteMutationFileName, getBulkDeleteMutationHookName, getBulkUpdateMutationFileName, getBulkUpdateMutationHookName, getBulkUpsertMutationFileName, getBulkUpsertMutationHookName, getCreateMutationFileName, getCreateMutationHookName, getCreateMutationName, getDeleteMutationFileName, getDeleteMutationHookName, getDeleteMutationName, getPrimaryKeyInfo, getTableNames, getUpdateMutationFileName, getUpdateMutationHookName, getUpdateMutationName, hasValidPrimaryKey, lcFirst, } from './utils';
13
13
  function buildMutationResultType(mutationName, singularName, relationTypeName, selectType) {
14
14
  return typeLiteralWithProps([
15
15
  {
@@ -377,6 +377,185 @@ export function generateDeleteMutationHook(table, options = {}) {
377
377
  content: generateHookFileCode(table.description || `Delete mutation hook for ${typeName}`, statements),
378
378
  };
379
379
  }
380
+ function generateBulkMutationHook(table, op, options = {}) {
381
+ const { reactQueryEnabled = true, useCentralizedKeys = true } = options;
382
+ if (!reactQueryEnabled)
383
+ return null;
384
+ const mutationFieldName = (() => {
385
+ switch (op) {
386
+ case 'bulkCreate': return table.query?.bulkInsert;
387
+ case 'bulkUpsert': return table.query?.bulkUpsert;
388
+ case 'bulkUpdate': return table.query?.bulkUpdate;
389
+ case 'bulkDelete': return table.query?.bulkDelete;
390
+ }
391
+ })();
392
+ if (!mutationFieldName)
393
+ return null;
394
+ const { typeName, singularName } = getTableNames(table);
395
+ const hookName = (() => {
396
+ switch (op) {
397
+ case 'bulkCreate': return getBulkCreateMutationHookName(table);
398
+ case 'bulkUpsert': return getBulkUpsertMutationHookName(table);
399
+ case 'bulkUpdate': return getBulkUpdateMutationHookName(table);
400
+ case 'bulkDelete': return getBulkDeleteMutationHookName(table);
401
+ }
402
+ })();
403
+ const fileName = (() => {
404
+ switch (op) {
405
+ case 'bulkCreate': return getBulkCreateMutationFileName(table);
406
+ case 'bulkUpsert': return getBulkUpsertMutationFileName(table);
407
+ case 'bulkUpdate': return getBulkUpdateMutationFileName(table);
408
+ case 'bulkDelete': return getBulkDeleteMutationFileName(table);
409
+ }
410
+ })();
411
+ const keysName = `${lcFirst(typeName)}Keys`;
412
+ const mutationKeysName = `${lcFirst(typeName)}MutationKeys`;
413
+ const selectTypeName = `${typeName}Select`;
414
+ const relationTypeName = `${typeName}WithRelations`;
415
+ const createInputTypeName = `Create${typeName}Input`;
416
+ const patchTypeName = `${typeName}Patch`;
417
+ const filterTypeName = `${typeName}Filter`;
418
+ const statements = [];
419
+ // Imports
420
+ statements.push(createImportDeclaration('@tanstack/react-query', [
421
+ 'useMutation',
422
+ 'useQueryClient',
423
+ ]));
424
+ statements.push(createImportDeclaration('@tanstack/react-query', ['UseMutationOptions', 'UseMutationResult'], true));
425
+ statements.push(createImportDeclaration('../client', ['getClient']));
426
+ statements.push(createImportDeclaration('../selection', ['buildSelectionArgs']));
427
+ statements.push(createImportDeclaration('../selection', ['SelectionConfig'], true));
428
+ if (useCentralizedKeys) {
429
+ statements.push(createImportDeclaration('../query-keys', [keysName]));
430
+ statements.push(createImportDeclaration('../mutation-keys', [mutationKeysName]));
431
+ }
432
+ // Determine which types to import
433
+ const typeImports = [selectTypeName, relationTypeName];
434
+ if (op === 'bulkCreate' || op === 'bulkUpsert') {
435
+ typeImports.push(createInputTypeName);
436
+ }
437
+ if (op === 'bulkUpdate') {
438
+ typeImports.push(patchTypeName);
439
+ typeImports.push(filterTypeName);
440
+ }
441
+ if (op === 'bulkDelete') {
442
+ typeImports.push(filterTypeName);
443
+ }
444
+ statements.push(createImportDeclaration('../../orm/input-types', typeImports, true));
445
+ statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'BulkMutationResult', 'HookStrictSelect'], true));
446
+ // Re-exports
447
+ statements.push(createTypeReExport(typeImports, '../../orm/input-types'));
448
+ // Build the variable type for the mutationFn parameter
449
+ const varType = (() => {
450
+ switch (op) {
451
+ case 'bulkCreate':
452
+ return t.tsTypeLiteral([
453
+ t.tsPropertySignature(t.identifier('data'), t.tsTypeAnnotation(t.tsArrayType(t.tsIndexedAccessType(typeRef(createInputTypeName), t.tsLiteralType(t.stringLiteral(singularName)))))),
454
+ (() => {
455
+ const p = t.tsPropertySignature(t.identifier('onConflict'), t.tsTypeAnnotation(t.tsUnknownKeyword()));
456
+ p.optional = true;
457
+ return p;
458
+ })(),
459
+ ]);
460
+ case 'bulkUpsert':
461
+ return t.tsTypeLiteral([
462
+ t.tsPropertySignature(t.identifier('data'), t.tsTypeAnnotation(t.tsArrayType(t.tsIndexedAccessType(typeRef(createInputTypeName), t.tsLiteralType(t.stringLiteral(singularName)))))),
463
+ t.tsPropertySignature(t.identifier('onConflict'), t.tsTypeAnnotation(t.tsUnknownKeyword())),
464
+ ]);
465
+ case 'bulkUpdate':
466
+ return t.tsTypeLiteral([
467
+ t.tsPropertySignature(t.identifier('where'), t.tsTypeAnnotation(typeRef(filterTypeName))),
468
+ t.tsPropertySignature(t.identifier('data'), t.tsTypeAnnotation(typeRef(patchTypeName))),
469
+ ]);
470
+ case 'bulkDelete':
471
+ return t.tsTypeLiteral([
472
+ t.tsPropertySignature(t.identifier('where'), t.tsTypeAnnotation(typeRef(filterTypeName))),
473
+ ]);
474
+ }
475
+ })();
476
+ // Result type: BulkMutationResult<InferSelectResult<Relation, S>>
477
+ const bulkResultType = (sel) => typeRef('BulkMutationResult', [inferSelectResultType(relationTypeName, sel)]);
478
+ // Overload with fields
479
+ const o1ParamType = t.tsIntersectionType([
480
+ t.tsTypeLiteral([
481
+ t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(buildFieldsSelectionType(sRef(), selectTypeName))),
482
+ ]),
483
+ useMutationOptionsType(bulkResultType(sRef()), varType),
484
+ ]);
485
+ const o1 = exportDeclareFunction(hookName, createSTypeParam(selectTypeName), [createFunctionParam('params', o1ParamType)], useMutationResultType(bulkResultType(sRef()), varType));
486
+ addJSDocComment(o1, [
487
+ table.description || `Bulk ${op.replace('bulk', '').toLowerCase()} mutation hook for ${typeName}`,
488
+ ]);
489
+ statements.push(o1);
490
+ // Implementation
491
+ const implSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))));
492
+ const implParamType = t.tsIntersectionType([
493
+ t.tsTypeLiteral([implSelProp]),
494
+ omitType(typeRef('UseMutationOptions', [
495
+ t.tsAnyKeyword(),
496
+ typeRef('Error'),
497
+ varType,
498
+ ]), ['mutationFn']),
499
+ ]);
500
+ const body = [];
501
+ body.push(buildSelectionArgsCall(selectTypeName));
502
+ body.push(destructureParamsWithSelection('mutationOptions'));
503
+ body.push(voidStatement('_selection'));
504
+ body.push(constDecl('queryClient', callExpr('useQueryClient', [])));
505
+ const mutationKeyExpr = useCentralizedKeys
506
+ ? callExpr(t.memberExpression(t.identifier(mutationKeysName), t.identifier(op)), [])
507
+ : undefined;
508
+ // Build the ORM method call depending on the operation
509
+ const ormMethodName = op;
510
+ const mutationFnArgs = (() => {
511
+ switch (op) {
512
+ case 'bulkCreate':
513
+ return t.objectExpression([
514
+ shorthandProp('data'),
515
+ objectProp('onConflict', t.memberExpression(t.identifier('vars'), t.identifier('onConflict'))),
516
+ objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select'))),
517
+ ]);
518
+ case 'bulkUpsert':
519
+ return t.objectExpression([
520
+ shorthandProp('data'),
521
+ objectProp('onConflict', t.memberExpression(t.identifier('vars'), t.identifier('onConflict'))),
522
+ objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select'))),
523
+ ]);
524
+ case 'bulkUpdate':
525
+ return t.objectExpression([
526
+ objectProp('where', t.memberExpression(t.identifier('vars'), t.identifier('where'))),
527
+ shorthandProp('data'),
528
+ objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select'))),
529
+ ]);
530
+ case 'bulkDelete':
531
+ return t.objectExpression([
532
+ objectProp('where', t.memberExpression(t.identifier('vars'), t.identifier('where'))),
533
+ objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select'))),
534
+ ]);
535
+ }
536
+ })();
537
+ const varsParam = createFunctionParam('vars', varType);
538
+ const mutationFnExpr = t.arrowFunctionExpression([varsParam], getClientCallUnwrap(singularName, ormMethodName, mutationFnArgs));
539
+ // onSuccess: invalidate lists
540
+ const listKeyExpr = useCentralizedKeys
541
+ ? callExpr(t.memberExpression(t.identifier(keysName), t.identifier('lists')), [])
542
+ : t.arrayExpression([
543
+ t.stringLiteral(typeName.toLowerCase()),
544
+ t.stringLiteral('list'),
545
+ ]);
546
+ const onSuccessFn = t.arrowFunctionExpression([], t.blockStatement([
547
+ t.expressionStatement(callExpr(t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), [t.objectExpression([objectProp('queryKey', listKeyExpr)])])),
548
+ ]));
549
+ body.push(returnUseMutation(mutationFnExpr, [
550
+ objectProp('onSuccess', onSuccessFn),
551
+ spreadObj(t.identifier('mutationOptions')),
552
+ ], mutationKeyExpr));
553
+ statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType)], body));
554
+ return {
555
+ fileName,
556
+ content: generateHookFileCode(table.description || `Bulk ${op.replace('bulk', '').toLowerCase()} mutation hook for ${typeName}`, statements),
557
+ };
558
+ }
380
559
  export function generateAllMutationHooks(tables, options = {}) {
381
560
  const files = [];
382
561
  for (const table of tables) {
@@ -392,6 +571,14 @@ export function generateAllMutationHooks(tables, options = {}) {
392
571
  if (deleteHook) {
393
572
  files.push(deleteHook);
394
573
  }
574
+ // Bulk mutation hooks
575
+ const bulkOps = ['bulkCreate', 'bulkUpsert', 'bulkUpdate', 'bulkDelete'];
576
+ for (const op of bulkOps) {
577
+ const hook = generateBulkMutationHook(table, op, options);
578
+ if (hook) {
579
+ files.push(hook);
580
+ }
581
+ }
395
582
  }
396
583
  return files;
397
584
  }
@@ -128,12 +128,6 @@ export function generateCreateClientFile(tables, hasCustomQueries, hasCustomMuta
128
128
  statements.push(t.exportAllDeclaration(t.stringLiteral('./select-types')));
129
129
  // Re-export all models
130
130
  statements.push(t.exportAllDeclaration(t.stringLiteral('./models')));
131
- // Re-export NodeHttpAdapter when enabled (for use in any Node.js application)
132
- if (options?.nodeHttpAdapter) {
133
- statements.push(t.exportNamedDeclaration(null, [
134
- t.exportSpecifier(t.identifier('NodeHttpAdapter'), t.identifier('NodeHttpAdapter')),
135
- ], t.stringLiteral('./node-fetch')));
136
- }
137
131
  // Re-export custom operations
138
132
  if (hasCustomQueries) {
139
133
  statements.push(t.exportNamedDeclaration(null, [