@constructive-io/graphql-codegen 4.41.0 → 4.41.2
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/docs-generator.js +28 -0
- package/core/codegen/cli/executor-generator.d.ts +2 -6
- package/core/codegen/cli/executor-generator.js +12 -48
- package/core/codegen/cli/index.d.ts +0 -2
- package/core/codegen/cli/index.js +2 -16
- package/core/codegen/cli/utils-generator.d.ts +0 -8
- package/core/codegen/cli/utils-generator.js +0 -14
- package/core/codegen/hooks-docs-generator.js +26 -0
- package/core/codegen/orm/client-generator.d.ts +1 -3
- package/core/codegen/orm/client-generator.js +1 -1
- package/core/codegen/orm/docs-generator.js +55 -1
- package/core/codegen/orm/index.js +1 -1
- package/core/codegen/templates/select-types.ts +1 -1
- package/core/generate.js +2 -19
- package/esm/core/codegen/cli/docs-generator.js +28 -0
- package/esm/core/codegen/cli/executor-generator.d.ts +2 -6
- package/esm/core/codegen/cli/executor-generator.js +12 -48
- package/esm/core/codegen/cli/index.d.ts +0 -2
- package/esm/core/codegen/cli/index.js +3 -17
- package/esm/core/codegen/cli/utils-generator.d.ts +0 -8
- package/esm/core/codegen/cli/utils-generator.js +0 -13
- package/esm/core/codegen/hooks-docs-generator.js +27 -1
- package/esm/core/codegen/orm/client-generator.d.ts +1 -3
- package/esm/core/codegen/orm/client-generator.js +1 -1
- package/esm/core/codegen/orm/docs-generator.js +55 -1
- package/esm/core/codegen/orm/index.js +1 -1
- package/esm/core/generate.js +2 -19
- package/esm/types/config.d.ts +0 -18
- package/package.json +4 -4
- package/types/config.d.ts +0 -18
- package/core/codegen/templates/node-fetch.ts +0 -198
|
@@ -7,12 +7,8 @@ function createImportDeclaration(moduleSpecifier, namedImports, typeOnly = false
|
|
|
7
7
|
decl.importKind = typeOnly ? 'type' : 'value';
|
|
8
8
|
return decl;
|
|
9
9
|
}
|
|
10
|
-
export function generateExecutorFile(toolName
|
|
10
|
+
export function generateExecutorFile(toolName) {
|
|
11
11
|
const statements = [];
|
|
12
|
-
// Import NodeHttpAdapter for *.localhost subdomain routing
|
|
13
|
-
if (options?.nodeHttpAdapter) {
|
|
14
|
-
statements.push(createImportDeclaration('./node-fetch', ['NodeHttpAdapter']));
|
|
15
|
-
}
|
|
16
12
|
statements.push(createImportDeclaration('appstash', ['createConfigStore']));
|
|
17
13
|
statements.push(createImportDeclaration('../orm', ['createClient']));
|
|
18
14
|
statements.push(t.variableDeclaration('const', [
|
|
@@ -72,26 +68,12 @@ export function generateExecutorFile(toolName, options) {
|
|
|
72
68
|
]))),
|
|
73
69
|
])),
|
|
74
70
|
])),
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
t.
|
|
79
|
-
t.objectExpression([
|
|
80
|
-
t.objectProperty(t.identifier('adapter'), t.newExpression(t.identifier('NodeHttpAdapter'), [
|
|
81
|
-
t.memberExpression(t.identifier('ctx'), t.identifier('endpoint')),
|
|
82
|
-
t.identifier('headers'),
|
|
83
|
-
])),
|
|
84
|
-
]),
|
|
85
|
-
])),
|
|
86
|
-
]
|
|
87
|
-
: [
|
|
88
|
-
t.returnStatement(t.callExpression(t.identifier('createClient'), [
|
|
89
|
-
t.objectExpression([
|
|
90
|
-
t.objectProperty(t.identifier('endpoint'), t.memberExpression(t.identifier('ctx'), t.identifier('endpoint'))),
|
|
91
|
-
t.objectProperty(t.identifier('headers'), t.identifier('headers')),
|
|
92
|
-
]),
|
|
93
|
-
])),
|
|
71
|
+
t.returnStatement(t.callExpression(t.identifier('createClient'), [
|
|
72
|
+
t.objectExpression([
|
|
73
|
+
t.objectProperty(t.identifier('endpoint'), t.memberExpression(t.identifier('ctx'), t.identifier('endpoint'))),
|
|
74
|
+
t.objectProperty(t.identifier('headers'), t.identifier('headers')),
|
|
94
75
|
]),
|
|
76
|
+
])),
|
|
95
77
|
]);
|
|
96
78
|
const getClientFunc = t.functionDeclaration(t.identifier('getClient'), [contextNameParam], getClientBody);
|
|
97
79
|
statements.push(t.exportNamedDeclaration(getClientFunc));
|
|
@@ -102,12 +84,8 @@ export function generateExecutorFile(toolName, options) {
|
|
|
102
84
|
content: header + '\n' + code,
|
|
103
85
|
};
|
|
104
86
|
}
|
|
105
|
-
export function generateMultiTargetExecutorFile(toolName, targets
|
|
87
|
+
export function generateMultiTargetExecutorFile(toolName, targets) {
|
|
106
88
|
const statements = [];
|
|
107
|
-
// Import NodeHttpAdapter for *.localhost subdomain routing
|
|
108
|
-
if (options?.nodeHttpAdapter) {
|
|
109
|
-
statements.push(createImportDeclaration('./node-fetch', ['NodeHttpAdapter']));
|
|
110
|
-
}
|
|
111
89
|
statements.push(createImportDeclaration('appstash', ['createConfigStore']));
|
|
112
90
|
for (const target of targets) {
|
|
113
91
|
const aliasName = `create${target.name[0].toUpperCase()}${target.name.slice(1)}Client`;
|
|
@@ -198,26 +176,12 @@ export function generateMultiTargetExecutorFile(toolName, targets, options) {
|
|
|
198
176
|
]), t.blockStatement([
|
|
199
177
|
t.expressionStatement(t.assignmentExpression('=', t.identifier('endpoint'), t.logicalExpression('||', t.memberExpression(t.identifier('defaultEndpoints'), t.identifier('targetName'), true), t.stringLiteral('')))),
|
|
200
178
|
])),
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
t.
|
|
205
|
-
t.objectExpression([
|
|
206
|
-
t.objectProperty(t.identifier('adapter'), t.newExpression(t.identifier('NodeHttpAdapter'), [
|
|
207
|
-
t.identifier('endpoint'),
|
|
208
|
-
t.identifier('headers'),
|
|
209
|
-
])),
|
|
210
|
-
]),
|
|
211
|
-
])),
|
|
212
|
-
]
|
|
213
|
-
: [
|
|
214
|
-
t.returnStatement(t.callExpression(t.identifier('createFn'), [
|
|
215
|
-
t.objectExpression([
|
|
216
|
-
t.objectProperty(t.identifier('endpoint'), t.identifier('endpoint')),
|
|
217
|
-
t.objectProperty(t.identifier('headers'), t.identifier('headers')),
|
|
218
|
-
]),
|
|
219
|
-
])),
|
|
179
|
+
t.returnStatement(t.callExpression(t.identifier('createFn'), [
|
|
180
|
+
t.objectExpression([
|
|
181
|
+
t.objectProperty(t.identifier('endpoint'), t.identifier('endpoint')),
|
|
182
|
+
t.objectProperty(t.identifier('headers'), t.identifier('headers')),
|
|
220
183
|
]),
|
|
184
|
+
])),
|
|
221
185
|
]);
|
|
222
186
|
const getClientFunc = t.functionDeclaration(t.identifier('getClient'), [targetNameParam, contextNameParam], getClientBody);
|
|
223
187
|
statements.push(t.exportNamedDeclaration(getClientFunc));
|
|
@@ -39,8 +39,6 @@ export interface GenerateMultiTargetCliOptions {
|
|
|
39
39
|
toolName: string;
|
|
40
40
|
builtinNames?: BuiltinNames;
|
|
41
41
|
targets: MultiTargetCliTarget[];
|
|
42
|
-
/** Enable NodeHttpAdapter for *.localhost subdomain routing */
|
|
43
|
-
nodeHttpAdapter?: boolean;
|
|
44
42
|
/** Generate a runnable index.ts entry point */
|
|
45
43
|
entryPoint?: boolean;
|
|
46
44
|
}
|
|
@@ -5,7 +5,7 @@ import { generateExecutorFile, generateMultiTargetExecutorFile } from './executo
|
|
|
5
5
|
import { generateHelpersFile } from './helpers-generator';
|
|
6
6
|
import { generateAuthCommand, generateAuthCommandWithName, generateContextCommand, generateMultiTargetContextCommand, } from './infra-generator';
|
|
7
7
|
import { generateTableCommand } from './table-command-generator';
|
|
8
|
-
import { generateUtilsFile,
|
|
8
|
+
import { generateUtilsFile, generateEntryPointFile, generateEmbedderFile } from './utils-generator';
|
|
9
9
|
export function generateCli(options) {
|
|
10
10
|
const { tables, customOperations, config } = options;
|
|
11
11
|
const files = [];
|
|
@@ -13,11 +13,7 @@ export function generateCli(options) {
|
|
|
13
13
|
const toolName = typeof cliConfig === 'object' && cliConfig.toolName
|
|
14
14
|
? cliConfig.toolName
|
|
15
15
|
: 'app';
|
|
16
|
-
|
|
17
|
-
const useNodeHttpAdapter = !!config.nodeHttpAdapter;
|
|
18
|
-
const executorFile = generateExecutorFile(toolName, {
|
|
19
|
-
nodeHttpAdapter: useNodeHttpAdapter,
|
|
20
|
-
});
|
|
16
|
+
const executorFile = generateExecutorFile(toolName);
|
|
21
17
|
files.push(executorFile);
|
|
22
18
|
const utilsFile = generateUtilsFile();
|
|
23
19
|
files.push(utilsFile);
|
|
@@ -26,10 +22,6 @@ export function generateCli(options) {
|
|
|
26
22
|
if (hasAnyEmbeddings) {
|
|
27
23
|
files.push(generateEmbedderFile());
|
|
28
24
|
}
|
|
29
|
-
// Generate node HTTP adapter if configured (for *.localhost subdomain routing)
|
|
30
|
-
if (useNodeHttpAdapter) {
|
|
31
|
-
files.push(generateNodeFetchFile());
|
|
32
|
-
}
|
|
33
25
|
const contextFile = generateContextCommand(toolName);
|
|
34
26
|
files.push(contextFile);
|
|
35
27
|
const authFile = generateAuthCommand(toolName);
|
|
@@ -91,9 +83,7 @@ export function generateMultiTargetCli(options) {
|
|
|
91
83
|
endpoint: t.endpoint,
|
|
92
84
|
ormImportPath: t.ormImportPath,
|
|
93
85
|
}));
|
|
94
|
-
const executorFile = generateMultiTargetExecutorFile(toolName, executorInputs
|
|
95
|
-
nodeHttpAdapter: !!options.nodeHttpAdapter,
|
|
96
|
-
});
|
|
86
|
+
const executorFile = generateMultiTargetExecutorFile(toolName, executorInputs);
|
|
97
87
|
files.push(executorFile);
|
|
98
88
|
const utilsFile = generateUtilsFile();
|
|
99
89
|
files.push(utilsFile);
|
|
@@ -102,10 +92,6 @@ export function generateMultiTargetCli(options) {
|
|
|
102
92
|
if (hasAnyMtEmbeddings) {
|
|
103
93
|
files.push(generateEmbedderFile());
|
|
104
94
|
}
|
|
105
|
-
// Generate node HTTP adapter if configured (for *.localhost subdomain routing)
|
|
106
|
-
if (options.nodeHttpAdapter) {
|
|
107
|
-
files.push(generateNodeFetchFile());
|
|
108
|
-
}
|
|
109
95
|
const contextFile = generateMultiTargetContextCommand(toolName, builtinNames.context, targets.map((t) => ({ name: t.name, endpoint: t.endpoint })));
|
|
110
96
|
files.push(contextFile);
|
|
111
97
|
const authFile = generateAuthCommandWithName(toolName, builtinNames.auth);
|
|
@@ -8,14 +8,6 @@ import type { GeneratedFile } from './executor-generator';
|
|
|
8
8
|
* and mutation input parsing.
|
|
9
9
|
*/
|
|
10
10
|
export declare function generateUtilsFile(): GeneratedFile;
|
|
11
|
-
/**
|
|
12
|
-
* Generate a node-fetch.ts file with NodeHttpAdapter for CLI.
|
|
13
|
-
*
|
|
14
|
-
* Provides a GraphQLAdapter implementation using node:http/node:https
|
|
15
|
-
* instead of the Fetch API. This cleanly handles *.localhost subdomain
|
|
16
|
-
* routing (DNS resolution + Host header) without any global patching.
|
|
17
|
-
*/
|
|
18
|
-
export declare function generateNodeFetchFile(): GeneratedFile;
|
|
19
11
|
/**
|
|
20
12
|
* Generate an index.ts entry point file for the CLI.
|
|
21
13
|
*
|
|
@@ -40,19 +40,6 @@ export function generateUtilsFile() {
|
|
|
40
40
|
content: readTemplateFile('cli-utils.ts', 'CLI utility functions for type coercion and input handling'),
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
-
/**
|
|
44
|
-
* Generate a node-fetch.ts file with NodeHttpAdapter for CLI.
|
|
45
|
-
*
|
|
46
|
-
* Provides a GraphQLAdapter implementation using node:http/node:https
|
|
47
|
-
* instead of the Fetch API. This cleanly handles *.localhost subdomain
|
|
48
|
-
* routing (DNS resolution + Host header) without any global patching.
|
|
49
|
-
*/
|
|
50
|
-
export function generateNodeFetchFile() {
|
|
51
|
-
return {
|
|
52
|
-
fileName: 'node-fetch.ts',
|
|
53
|
-
content: readTemplateFile('node-fetch.ts', 'Node HTTP adapter for localhost subdomain routing'),
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
43
|
/**
|
|
57
44
|
* Generate an index.ts entry point file for the CLI.
|
|
58
45
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { toKebabCase } from 'inflekt';
|
|
2
2
|
import { buildSkillFile, buildSkillReference, formatArgType, fieldPlaceholder, pkPlaceholder, argPlaceholder, getReadmeHeader, getReadmeFooter, } from './docs-utils';
|
|
3
|
-
import { getTableNames, getScalarFields, getPrimaryKeyInfo, getListQueryHookName, getSingleQueryHookName, getCreateMutationHookName, getUpdateMutationHookName, getDeleteMutationHookName, hasValidPrimaryKey, ucFirst, } from './utils';
|
|
3
|
+
import { getTableNames, getScalarFields, getPrimaryKeyInfo, getListQueryHookName, getSingleQueryHookName, getCreateMutationHookName, getUpdateMutationHookName, getDeleteMutationHookName, getBulkCreateMutationHookName, getBulkUpsertMutationHookName, getBulkUpdateMutationHookName, getBulkDeleteMutationHookName, hasValidPrimaryKey, ucFirst, } from './utils';
|
|
4
4
|
function getCustomHookName(op) {
|
|
5
5
|
if (op.kind === 'query') {
|
|
6
6
|
return `use${ucFirst(op.name)}Query`;
|
|
@@ -47,6 +47,18 @@ export function generateHooksReadme(tables, customOperations, registry) {
|
|
|
47
47
|
lines.push(`| \`${getUpdateMutationHookName(table)}\` | Mutation | ${table.description || `Update a ${singularName}`} |`);
|
|
48
48
|
lines.push(`| \`${getDeleteMutationHookName(table)}\` | Mutation | ${table.description || `Delete a ${singularName}`} |`);
|
|
49
49
|
}
|
|
50
|
+
if (table.query?.bulkInsert) {
|
|
51
|
+
lines.push(`| \`${getBulkCreateMutationHookName(table)}\` | Mutation | Bulk create ${pluralName} |`);
|
|
52
|
+
}
|
|
53
|
+
if (table.query?.bulkUpsert) {
|
|
54
|
+
lines.push(`| \`${getBulkUpsertMutationHookName(table)}\` | Mutation | Bulk upsert ${pluralName} |`);
|
|
55
|
+
}
|
|
56
|
+
if (table.query?.bulkUpdate) {
|
|
57
|
+
lines.push(`| \`${getBulkUpdateMutationHookName(table)}\` | Mutation | Bulk update ${pluralName} |`);
|
|
58
|
+
}
|
|
59
|
+
if (table.query?.bulkDelete) {
|
|
60
|
+
lines.push(`| \`${getBulkDeleteMutationHookName(table)}\` | Mutation | Bulk delete ${pluralName} |`);
|
|
61
|
+
}
|
|
50
62
|
}
|
|
51
63
|
for (const op of customOperations) {
|
|
52
64
|
lines.push(`| \`${getCustomHookName(op)}\` | ${ucFirst(op.kind)} | ${op.description || op.name} |`);
|
|
@@ -80,6 +92,14 @@ export function generateHooksReadme(tables, customOperations, registry) {
|
|
|
80
92
|
lines.push(` selection: { fields: { ${pk.name}: true } },`);
|
|
81
93
|
lines.push('});');
|
|
82
94
|
lines.push(`create({ ${scalarFields.filter((f) => f.name !== pk.name && f.name !== 'nodeId' && f.name !== 'createdAt' && f.name !== 'updatedAt').map((f) => `${f.name}: ${fieldPlaceholder(f)}`).join(', ')} });`);
|
|
95
|
+
if (table.query?.bulkInsert) {
|
|
96
|
+
lines.push('');
|
|
97
|
+
lines.push(`// Bulk create ${pluralName}`);
|
|
98
|
+
lines.push(`const { mutate: bulkCreate } = ${getBulkCreateMutationHookName(table)}({`);
|
|
99
|
+
lines.push(` selection: { fields: { ${pk.name}: true } },`);
|
|
100
|
+
lines.push('});');
|
|
101
|
+
lines.push(`bulkCreate({ data: [{ ${scalarFields.filter((f) => f.name !== pk.name && f.name !== 'nodeId' && f.name !== 'createdAt' && f.name !== 'updatedAt').map((f) => `${f.name}: ${fieldPlaceholder(f)}`).join(', ')} }] });`);
|
|
102
|
+
}
|
|
83
103
|
lines.push('```');
|
|
84
104
|
lines.push('');
|
|
85
105
|
}
|
|
@@ -150,6 +170,7 @@ export function generateHooksAgentsDocs(tables, customOperations) {
|
|
|
150
170
|
lines.push('');
|
|
151
171
|
lines.push('- Query hooks: `use<PluralName>Query`, `use<SingularName>Query`');
|
|
152
172
|
lines.push('- Mutation hooks: `useCreate<Name>Mutation`, `useUpdate<Name>Mutation`, `useDelete<Name>Mutation`');
|
|
173
|
+
lines.push('- Bulk mutation hooks (when enabled): `useBulkCreate<Name>Mutation`, `useBulkUpsert<Name>Mutation`, `useBulkUpdate<Name>Mutation`, `useBulkDelete<Name>Mutation`');
|
|
153
174
|
lines.push('- All hooks accept a `selection` parameter to pick fields');
|
|
154
175
|
lines.push('');
|
|
155
176
|
lines.push('## Boundaries');
|
|
@@ -195,6 +216,10 @@ export function generateHooksSkills(tables, customOperations, targetName, regist
|
|
|
195
216
|
`${getDeleteMutationHookName(table)}({})`,
|
|
196
217
|
]
|
|
197
218
|
: []),
|
|
219
|
+
...(table.query?.bulkInsert ? [`${getBulkCreateMutationHookName(table)}() — bulk create with data array`] : []),
|
|
220
|
+
...(table.query?.bulkUpsert ? [`${getBulkUpsertMutationHookName(table)}() — bulk upsert with onConflict`] : []),
|
|
221
|
+
...(table.query?.bulkUpdate ? [`${getBulkUpdateMutationHookName(table)}() — bulk update with where + data`] : []),
|
|
222
|
+
...(table.query?.bulkDelete ? [`${getBulkDeleteMutationHookName(table)}() — bulk delete with where`] : []),
|
|
198
223
|
],
|
|
199
224
|
examples: [
|
|
200
225
|
{
|
|
@@ -270,6 +295,7 @@ export function generateHooksSkills(tables, customOperations, targetName, regist
|
|
|
270
295
|
'',
|
|
271
296
|
`// Query hooks: use<Model>Query, use<Model>sQuery`,
|
|
272
297
|
`// Mutation hooks: useCreate<Model>Mutation, useUpdate<Model>Mutation, useDelete<Model>Mutation`,
|
|
298
|
+
`// Bulk mutation hooks (when enabled): useBulkCreate<Model>Mutation, useBulkUpsert<Model>Mutation, etc.`,
|
|
273
299
|
'',
|
|
274
300
|
`const { data, isLoading } = ${hookExamples[0] || 'useModelQuery'}({`,
|
|
275
301
|
` selection: { fields: { id: true } },`,
|
|
@@ -31,6 +31,4 @@ export declare function generateSelectTypesFile(): GeneratedClientFile;
|
|
|
31
31
|
/**
|
|
32
32
|
* Generate the main index.ts with createClient factory
|
|
33
33
|
*/
|
|
34
|
-
export declare function generateCreateClientFile(tables: Table[], hasCustomQueries: boolean, hasCustomMutations: boolean
|
|
35
|
-
nodeHttpAdapter?: boolean;
|
|
36
|
-
}): GeneratedClientFile;
|
|
34
|
+
export declare function generateCreateClientFile(tables: Table[], hasCustomQueries: boolean, hasCustomMutations: boolean): GeneratedClientFile;
|
|
@@ -86,7 +86,7 @@ function createImportDeclaration(moduleSpecifier, namedImports, typeOnly = false
|
|
|
86
86
|
/**
|
|
87
87
|
* Generate the main index.ts with createClient factory
|
|
88
88
|
*/
|
|
89
|
-
export function generateCreateClientFile(tables, hasCustomQueries, hasCustomMutations
|
|
89
|
+
export function generateCreateClientFile(tables, hasCustomQueries, hasCustomMutations) {
|
|
90
90
|
const statements = [];
|
|
91
91
|
// Add imports
|
|
92
92
|
// Import OrmClient (value) and OrmClientConfig (type) separately
|
|
@@ -21,7 +21,17 @@ export function generateOrmReadme(tables, customOperations, registry) {
|
|
|
21
21
|
lines.push('|-------|------------|');
|
|
22
22
|
for (const table of tables) {
|
|
23
23
|
const { singularName } = getTableNames(table);
|
|
24
|
-
|
|
24
|
+
const bulkOps = [];
|
|
25
|
+
if (table.query?.bulkInsert)
|
|
26
|
+
bulkOps.push('bulkCreate');
|
|
27
|
+
if (table.query?.bulkUpsert)
|
|
28
|
+
bulkOps.push('bulkUpsert');
|
|
29
|
+
if (table.query?.bulkUpdate)
|
|
30
|
+
bulkOps.push('bulkUpdate');
|
|
31
|
+
if (table.query?.bulkDelete)
|
|
32
|
+
bulkOps.push('bulkDelete');
|
|
33
|
+
const ops = ['findMany', 'findOne', 'create', 'update', 'delete', ...bulkOps].join(', ');
|
|
34
|
+
lines.push(`| \`${singularName}\` | ${ops} |`);
|
|
25
35
|
}
|
|
26
36
|
lines.push('');
|
|
27
37
|
if (tables.length > 0) {
|
|
@@ -62,6 +72,26 @@ export function generateOrmReadme(tables, customOperations, registry) {
|
|
|
62
72
|
lines.push('');
|
|
63
73
|
lines.push(`// Delete`);
|
|
64
74
|
lines.push(`const deleted = await db.${singularName}.delete({ where: { ${pk.name}: ${pkPlaceholder(pk)} } }).execute();`);
|
|
75
|
+
if (table.query?.bulkInsert) {
|
|
76
|
+
lines.push('');
|
|
77
|
+
lines.push(`// Bulk Create`);
|
|
78
|
+
lines.push(`const bulkCreated = await db.${singularName}.bulkCreate({ data: [{ ${editableFields.map((f) => `${f.name}: ${fieldPlaceholder(f)}`).join(', ')} }], select: { ${pk.name}: true } }).execute();`);
|
|
79
|
+
}
|
|
80
|
+
if (table.query?.bulkUpsert) {
|
|
81
|
+
lines.push('');
|
|
82
|
+
lines.push(`// Bulk Upsert`);
|
|
83
|
+
lines.push(`const bulkUpserted = await db.${singularName}.bulkUpsert({ data: [{ ${editableFields.map((f) => `${f.name}: ${fieldPlaceholder(f)}`).join(', ')} }], onConflict: { constraint: 'PRIMARY_KEY', action: 'UPDATE' }, select: { ${pk.name}: true } }).execute();`);
|
|
84
|
+
}
|
|
85
|
+
if (table.query?.bulkUpdate) {
|
|
86
|
+
lines.push('');
|
|
87
|
+
lines.push(`// Bulk Update`);
|
|
88
|
+
lines.push(`const bulkUpdated = await db.${singularName}.bulkUpdate({ where: { ${editableFields[0]?.name || 'field'}: { equalTo: ${editableFields[0] ? fieldPlaceholder(editableFields[0]) : "'<String>'"} } }, data: { ${editableFields[0]?.name || 'field'}: ${editableFields[0] ? fieldPlaceholder(editableFields[0]) : "'<String>'"} }, select: { ${pk.name}: true } }).execute();`);
|
|
89
|
+
}
|
|
90
|
+
if (table.query?.bulkDelete) {
|
|
91
|
+
lines.push('');
|
|
92
|
+
lines.push(`// Bulk Delete`);
|
|
93
|
+
lines.push(`const bulkDeleted = await db.${singularName}.bulkDelete({ where: { ${pk.name}: { equalTo: ${pkPlaceholder(pk)} } } }).execute();`);
|
|
94
|
+
}
|
|
65
95
|
lines.push('```');
|
|
66
96
|
lines.push('');
|
|
67
97
|
const ormSpecialGroups = categorizeSpecialFields(table);
|
|
@@ -165,6 +195,7 @@ export function generateOrmAgentsDocs(tables, customOperations) {
|
|
|
165
195
|
lines.push('');
|
|
166
196
|
lines.push('- Access models via `db.<ModelName>` (e.g. `db.User`)');
|
|
167
197
|
lines.push('- CRUD methods: `findMany`, `findOne`, `create`, `update`, `delete`');
|
|
198
|
+
lines.push('- Bulk methods (when enabled via smart tags): `bulkCreate`, `bulkUpsert`, `bulkUpdate`, `bulkDelete`');
|
|
168
199
|
lines.push('- Chain `.execute().unwrap()` to run and throw on error, or `.execute()` alone for discriminated union result');
|
|
169
200
|
lines.push('- Custom operations via `db.query.<name>` or `db.mutation.<name>`');
|
|
170
201
|
lines.push('');
|
|
@@ -195,6 +226,19 @@ export function generateOrmSkills(tables, customOperations, targetName, registry
|
|
|
195
226
|
? ormSkillBaseDesc + '\n\n' +
|
|
196
227
|
ormSkillSpecialGroups.map((g) => `**${g.label}:** ${g.fields.map((f) => `\`${f.name}\``).join(', ')}\n${g.description}`).join('\n\n')
|
|
197
228
|
: ormSkillBaseDesc;
|
|
229
|
+
const bulkUsageLines = [];
|
|
230
|
+
if (table.query?.bulkInsert) {
|
|
231
|
+
bulkUsageLines.push(`db.${modelName}.bulkCreate({ data: [...], select: { id: true } }).execute()`);
|
|
232
|
+
}
|
|
233
|
+
if (table.query?.bulkUpsert) {
|
|
234
|
+
bulkUsageLines.push(`db.${modelName}.bulkUpsert({ data: [...], onConflict: { constraint: '...', action: 'UPDATE' }, select: { id: true } }).execute()`);
|
|
235
|
+
}
|
|
236
|
+
if (table.query?.bulkUpdate) {
|
|
237
|
+
bulkUsageLines.push(`db.${modelName}.bulkUpdate({ where: {...}, data: {...}, select: { id: true } }).execute()`);
|
|
238
|
+
}
|
|
239
|
+
if (table.query?.bulkDelete) {
|
|
240
|
+
bulkUsageLines.push(`db.${modelName}.bulkDelete({ where: {...} }).execute()`);
|
|
241
|
+
}
|
|
198
242
|
files.push({
|
|
199
243
|
fileName: `${skillName}/references/${refName}.md`,
|
|
200
244
|
content: buildSkillReference({
|
|
@@ -207,6 +251,7 @@ export function generateOrmSkills(tables, customOperations, targetName, registry
|
|
|
207
251
|
`db.${modelName}.create({ data: { ${editableFields.map((f) => `${f.name}: ${fieldPlaceholder(f)}`).join(', ')} }, select: { id: true } }).execute()`,
|
|
208
252
|
`db.${modelName}.update({ where: { ${pk.name}: ${pkPlaceholder(pk)} }, data: { ${editableFields[0]?.name || 'field'}: ${editableFields[0] ? fieldPlaceholder(editableFields[0]) : "'<String>'"} }, select: { id: true } }).execute()`,
|
|
209
253
|
`db.${modelName}.delete({ where: { ${pk.name}: ${pkPlaceholder(pk)} } }).execute()`,
|
|
254
|
+
...bulkUsageLines,
|
|
210
255
|
],
|
|
211
256
|
examples: [
|
|
212
257
|
{
|
|
@@ -256,6 +301,7 @@ export function generateOrmSkills(tables, customOperations, targetName, registry
|
|
|
256
301
|
}
|
|
257
302
|
// Generate the overview SKILL.md
|
|
258
303
|
const tableNames = tables.map((t) => lcFirst(getTableNames(t).singularName));
|
|
304
|
+
const hasBulkTables = tables.some((t) => t.query?.bulkInsert || t.query?.bulkUpsert || t.query?.bulkUpdate || t.query?.bulkDelete);
|
|
259
305
|
files.push({
|
|
260
306
|
fileName: `${skillName}/SKILL.md`,
|
|
261
307
|
content: buildSkillFile({
|
|
@@ -272,6 +318,14 @@ export function generateOrmSkills(tables, customOperations, targetName, registry
|
|
|
272
318
|
`db.<model>.create({ data: { ... }, select: { id: true } }).execute()`,
|
|
273
319
|
`db.<model>.update({ where: { id: '<UUID>' }, data: { ... }, select: { id: true } }).execute()`,
|
|
274
320
|
`db.<model>.delete({ where: { id: '<UUID>' } }).execute()`,
|
|
321
|
+
...(hasBulkTables ? [
|
|
322
|
+
'',
|
|
323
|
+
`// Bulk operations (on tables with @behavior +bulkInsert/+bulkUpsert/+bulkUpdate/+bulkDelete)`,
|
|
324
|
+
`db.<model>.bulkCreate({ data: [...], select: { id: true } }).execute()`,
|
|
325
|
+
`db.<model>.bulkUpsert({ data: [...], onConflict: { constraint: '...', action: 'UPDATE' }, select: { id: true } }).execute()`,
|
|
326
|
+
`db.<model>.bulkUpdate({ where: {...}, data: {...}, select: { id: true } }).execute()`,
|
|
327
|
+
`db.<model>.bulkDelete({ where: {...} }).execute()`,
|
|
328
|
+
] : []),
|
|
275
329
|
],
|
|
276
330
|
examples: [
|
|
277
331
|
{
|
|
@@ -90,7 +90,7 @@ export function generateOrm(options) {
|
|
|
90
90
|
const typesBarrel = generateTypesBarrel(useSharedTypes);
|
|
91
91
|
files.push({ path: typesBarrel.fileName, content: typesBarrel.content });
|
|
92
92
|
// 7. Generate main index.ts with createClient
|
|
93
|
-
const indexFile = generateCreateClientFile(tables, hasCustomQueries, hasCustomMutations
|
|
93
|
+
const indexFile = generateCreateClientFile(tables, hasCustomQueries, hasCustomMutations);
|
|
94
94
|
files.push({ path: indexFile.fileName, content: indexFile.content });
|
|
95
95
|
return {
|
|
96
96
|
files,
|
package/esm/core/generate.js
CHANGED
|
@@ -47,9 +47,6 @@ export async function generate(options = {}, internalOptions) {
|
|
|
47
47
|
const runReactQuery = config.reactQuery ?? false;
|
|
48
48
|
const runCli = internalOptions?.skipCli ? false : !!config.cli;
|
|
49
49
|
const runOrm = runReactQuery || !!config.cli || (options.orm !== undefined ? !!options.orm : false);
|
|
50
|
-
// Auto-enable nodeHttpAdapter when CLI is enabled, unless explicitly set to false
|
|
51
|
-
const useNodeHttpAdapter = options.nodeHttpAdapter === true ||
|
|
52
|
-
(runCli && options.nodeHttpAdapter !== false);
|
|
53
50
|
const schemaEnabled = !!options.schema?.enabled;
|
|
54
51
|
if (!schemaEnabled && !runReactQuery && !runOrm && !runCli) {
|
|
55
52
|
return {
|
|
@@ -184,22 +181,13 @@ export async function generate(options = {}, internalOptions) {
|
|
|
184
181
|
mutations: customOperations.mutations,
|
|
185
182
|
typeRegistry: customOperations.typeRegistry,
|
|
186
183
|
},
|
|
187
|
-
config
|
|
184
|
+
config,
|
|
188
185
|
sharedTypesPath: bothEnabled ? '..' : undefined,
|
|
189
186
|
});
|
|
190
187
|
filesToWrite.push(...files.map((file) => ({
|
|
191
188
|
...file,
|
|
192
189
|
path: path.posix.join('orm', file.path),
|
|
193
190
|
})));
|
|
194
|
-
// Generate NodeHttpAdapter in ORM output when enabled
|
|
195
|
-
if (useNodeHttpAdapter) {
|
|
196
|
-
const { generateNodeFetchFile } = await import('./codegen/cli/utils-generator');
|
|
197
|
-
const nodeFetchFile = generateNodeFetchFile();
|
|
198
|
-
filesToWrite.push({
|
|
199
|
-
path: path.posix.join('orm', nodeFetchFile.fileName),
|
|
200
|
-
content: nodeFetchFile.content,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
191
|
}
|
|
204
192
|
// Generate CLI commands
|
|
205
193
|
if (runCli) {
|
|
@@ -210,7 +198,7 @@ export async function generate(options = {}, internalOptions) {
|
|
|
210
198
|
queries: customOperations.queries,
|
|
211
199
|
mutations: customOperations.mutations,
|
|
212
200
|
},
|
|
213
|
-
config
|
|
201
|
+
config,
|
|
214
202
|
typeRegistry: customOperations.typeRegistry,
|
|
215
203
|
});
|
|
216
204
|
filesToWrite.push(...files.map((file) => ({
|
|
@@ -540,16 +528,11 @@ export async function generateMulti(options) {
|
|
|
540
528
|
if (useUnifiedCli && cliTargets.length > 0 && !dryRun) {
|
|
541
529
|
const cliConfig = typeof unifiedCli === 'object' ? unifiedCli : {};
|
|
542
530
|
const toolName = cliConfig.toolName ?? 'app';
|
|
543
|
-
// Auto-enable nodeHttpAdapter for unified CLI unless explicitly disabled
|
|
544
|
-
// Check first target config for explicit nodeHttpAdapter setting
|
|
545
531
|
const firstTargetConfig = configs[names[0]];
|
|
546
|
-
const multiNodeHttpAdapter = firstTargetConfig?.nodeHttpAdapter === true ||
|
|
547
|
-
(firstTargetConfig?.nodeHttpAdapter !== false);
|
|
548
532
|
const { files } = generateMultiTargetCli({
|
|
549
533
|
toolName,
|
|
550
534
|
builtinNames: cliConfig.builtinNames,
|
|
551
535
|
targets: cliTargets,
|
|
552
|
-
nodeHttpAdapter: multiNodeHttpAdapter,
|
|
553
536
|
entryPoint: cliConfig.entryPoint,
|
|
554
537
|
});
|
|
555
538
|
const cliFilesToWrite = files.map((file) => ({
|
package/esm/types/config.d.ts
CHANGED
|
@@ -317,24 +317,6 @@ export interface GraphQLSDKConfigTarget {
|
|
|
317
317
|
* @default false
|
|
318
318
|
*/
|
|
319
319
|
orm?: boolean;
|
|
320
|
-
/**
|
|
321
|
-
* Enable NodeHttpAdapter generation for Node.js applications.
|
|
322
|
-
* When true, generates a node-fetch.ts with NodeHttpAdapter (implements GraphQLAdapter)
|
|
323
|
-
* using node:http/node:https for requests, enabling local development
|
|
324
|
-
* with subdomain-based routing (e.g. auth.localhost:3000).
|
|
325
|
-
* No global patching — the adapter is passed to createClient via the adapter option.
|
|
326
|
-
*
|
|
327
|
-
* When CLI generation is enabled (`cli: true`), this is auto-enabled unless
|
|
328
|
-
* explicitly set to `false`.
|
|
329
|
-
*
|
|
330
|
-
* Can also be used standalone with the ORM client for any Node.js application:
|
|
331
|
-
* ```ts
|
|
332
|
-
* import { NodeHttpAdapter } from './orm/node-fetch';
|
|
333
|
-
* const db = createClient({ adapter: new NodeHttpAdapter(endpoint, headers) });
|
|
334
|
-
* ```
|
|
335
|
-
* @default false
|
|
336
|
-
*/
|
|
337
|
-
nodeHttpAdapter?: boolean;
|
|
338
320
|
/**
|
|
339
321
|
* Whether to generate React Query hooks
|
|
340
322
|
* When enabled, generates React Query hooks to {output}/hooks
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constructive-io/graphql-codegen",
|
|
3
|
-
"version": "4.41.
|
|
3
|
+
"version": "4.41.2",
|
|
4
4
|
"description": "GraphQL SDK generator for Constructive databases with React Query hooks",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"graphql",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"@0no-co/graphql.web": "^1.1.2",
|
|
57
57
|
"@babel/generator": "^7.29.1",
|
|
58
58
|
"@babel/types": "^7.29.0",
|
|
59
|
-
"@constructive-io/graphql-query": "^3.24.
|
|
59
|
+
"@constructive-io/graphql-query": "^3.24.1",
|
|
60
60
|
"@constructive-io/graphql-types": "^3.9.1",
|
|
61
61
|
"@inquirerer/utils": "^3.3.5",
|
|
62
62
|
"@pgpmjs/core": "^6.17.1",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"deepmerge": "^4.3.1",
|
|
65
65
|
"find-and-require-package-json": "^0.9.1",
|
|
66
66
|
"gql-ast": "^3.9.1",
|
|
67
|
-
"graphile-schema": "^1.19.
|
|
67
|
+
"graphile-schema": "^1.19.5",
|
|
68
68
|
"graphql": "16.13.0",
|
|
69
69
|
"inflekt": "^0.7.1",
|
|
70
70
|
"inquirerer": "^4.7.0",
|
|
@@ -100,5 +100,5 @@
|
|
|
100
100
|
"tsx": "^4.21.0",
|
|
101
101
|
"typescript": "^5.9.3"
|
|
102
102
|
},
|
|
103
|
-
"gitHead": "
|
|
103
|
+
"gitHead": "42ff6bf5fa2e400bb63f78745a1f5a76209677b6"
|
|
104
104
|
}
|
package/types/config.d.ts
CHANGED
|
@@ -317,24 +317,6 @@ export interface GraphQLSDKConfigTarget {
|
|
|
317
317
|
* @default false
|
|
318
318
|
*/
|
|
319
319
|
orm?: boolean;
|
|
320
|
-
/**
|
|
321
|
-
* Enable NodeHttpAdapter generation for Node.js applications.
|
|
322
|
-
* When true, generates a node-fetch.ts with NodeHttpAdapter (implements GraphQLAdapter)
|
|
323
|
-
* using node:http/node:https for requests, enabling local development
|
|
324
|
-
* with subdomain-based routing (e.g. auth.localhost:3000).
|
|
325
|
-
* No global patching — the adapter is passed to createClient via the adapter option.
|
|
326
|
-
*
|
|
327
|
-
* When CLI generation is enabled (`cli: true`), this is auto-enabled unless
|
|
328
|
-
* explicitly set to `false`.
|
|
329
|
-
*
|
|
330
|
-
* Can also be used standalone with the ORM client for any Node.js application:
|
|
331
|
-
* ```ts
|
|
332
|
-
* import { NodeHttpAdapter } from './orm/node-fetch';
|
|
333
|
-
* const db = createClient({ adapter: new NodeHttpAdapter(endpoint, headers) });
|
|
334
|
-
* ```
|
|
335
|
-
* @default false
|
|
336
|
-
*/
|
|
337
|
-
nodeHttpAdapter?: boolean;
|
|
338
320
|
/**
|
|
339
321
|
* Whether to generate React Query hooks
|
|
340
322
|
* When enabled, generates React Query hooks to {output}/hooks
|