@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.
- package/core/codegen/cli/table-command-generator.js +141 -2
- package/core/codegen/mutation-keys.js +18 -0
- package/core/codegen/mutations.js +187 -0
- package/core/codegen/orm/client-generator.js +0 -6
- package/core/codegen/orm/model-generator.js +167 -5
- package/core/codegen/orm/select-types.d.ts +2 -1
- package/core/codegen/queries.js +1 -1
- package/core/codegen/templates/cli-utils.ts +4 -2
- package/core/codegen/templates/query-builder.ts +170 -1
- package/core/codegen/templates/select-types.ts +30 -1
- package/core/codegen/utils.d.ts +8 -0
- package/core/codegen/utils.js +39 -0
- package/esm/core/codegen/cli/table-command-generator.js +141 -2
- package/esm/core/codegen/mutation-keys.js +18 -0
- package/esm/core/codegen/mutations.js +188 -1
- package/esm/core/codegen/orm/client-generator.js +0 -6
- package/esm/core/codegen/orm/model-generator.js +168 -6
- package/esm/core/codegen/orm/select-types.d.ts +2 -1
- package/esm/core/codegen/queries.js +1 -1
- package/esm/core/codegen/utils.d.ts +8 -0
- package/esm/core/codegen/utils.js +31 -0
- package/esm/types/schema.d.ts +8 -0
- package/package.json +4 -4
- package/types/schema.d.ts +8 -0
|
@@ -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
|
-
|
|
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, [
|