@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.
Files changed (31) hide show
  1. package/core/codegen/cli/docs-generator.js +28 -0
  2. package/core/codegen/cli/executor-generator.d.ts +2 -6
  3. package/core/codegen/cli/executor-generator.js +12 -48
  4. package/core/codegen/cli/index.d.ts +0 -2
  5. package/core/codegen/cli/index.js +2 -16
  6. package/core/codegen/cli/utils-generator.d.ts +0 -8
  7. package/core/codegen/cli/utils-generator.js +0 -14
  8. package/core/codegen/hooks-docs-generator.js +26 -0
  9. package/core/codegen/orm/client-generator.d.ts +1 -3
  10. package/core/codegen/orm/client-generator.js +1 -1
  11. package/core/codegen/orm/docs-generator.js +55 -1
  12. package/core/codegen/orm/index.js +1 -1
  13. package/core/codegen/templates/select-types.ts +1 -1
  14. package/core/generate.js +2 -19
  15. package/esm/core/codegen/cli/docs-generator.js +28 -0
  16. package/esm/core/codegen/cli/executor-generator.d.ts +2 -6
  17. package/esm/core/codegen/cli/executor-generator.js +12 -48
  18. package/esm/core/codegen/cli/index.d.ts +0 -2
  19. package/esm/core/codegen/cli/index.js +3 -17
  20. package/esm/core/codegen/cli/utils-generator.d.ts +0 -8
  21. package/esm/core/codegen/cli/utils-generator.js +0 -13
  22. package/esm/core/codegen/hooks-docs-generator.js +27 -1
  23. package/esm/core/codegen/orm/client-generator.d.ts +1 -3
  24. package/esm/core/codegen/orm/client-generator.js +1 -1
  25. package/esm/core/codegen/orm/docs-generator.js +55 -1
  26. package/esm/core/codegen/orm/index.js +1 -1
  27. package/esm/core/generate.js +2 -19
  28. package/esm/types/config.d.ts +0 -18
  29. package/package.json +4 -4
  30. package/types/config.d.ts +0 -18
  31. 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, options) {
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
- // Build createClient config — use NodeHttpAdapter for *.localhost endpoints
76
- ...(options?.nodeHttpAdapter
77
- ? [
78
- t.returnStatement(t.callExpression(t.identifier('createClient'), [
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, options) {
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
- // Build createClient config — use NodeHttpAdapter for *.localhost endpoints
202
- ...(options?.nodeHttpAdapter
203
- ? [
204
- t.returnStatement(t.callExpression(t.identifier('createFn'), [
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, generateNodeFetchFile, generateEntryPointFile, generateEmbedderFile } from './utils-generator';
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
- // Use top-level nodeHttpAdapter from config (auto-enabled for CLI by generate.ts)
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, options?: {
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, options) {
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
- lines.push(`| \`${singularName}\` | findMany, findOne, create, update, delete |`);
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, { nodeHttpAdapter: !!options.config.nodeHttpAdapter });
93
+ const indexFile = generateCreateClientFile(tables, hasCustomQueries, hasCustomMutations);
94
94
  files.push({ path: indexFile.fileName, content: indexFile.content });
95
95
  return {
96
96
  files,
@@ -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: { ...config, nodeHttpAdapter: useNodeHttpAdapter },
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: { ...config, nodeHttpAdapter: useNodeHttpAdapter },
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) => ({
@@ -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.0",
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.0",
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.4",
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": "70f9d2052fe9218c65b82e896d1660d4dc6a51c8"
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