@constructive-io/graphql-codegen 4.40.6 → 4.41.1
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/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/table-command-generator.js +141 -2
- package/core/codegen/cli/utils-generator.d.ts +0 -8
- package/core/codegen/cli/utils-generator.js +0 -14
- package/core/codegen/mutation-keys.js +18 -0
- package/core/codegen/mutations.js +187 -0
- package/core/codegen/orm/client-generator.d.ts +1 -3
- package/core/codegen/orm/client-generator.js +1 -7
- package/core/codegen/orm/index.js +1 -1
- 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/core/generate.js +2 -19
- 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/table-command-generator.js +141 -2
- 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/mutation-keys.js +18 -0
- package/esm/core/codegen/mutations.js +188 -1
- package/esm/core/codegen/orm/client-generator.d.ts +1 -3
- package/esm/core/codegen/orm/client-generator.js +1 -7
- package/esm/core/codegen/orm/index.js +1 -1
- 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/core/generate.js +2 -19
- package/esm/types/config.d.ts +0 -18
- package/esm/types/schema.d.ts +8 -0
- package/package.json +3 -3
- package/types/config.d.ts +0 -18
- package/types/schema.d.ts +8 -0
- package/core/codegen/templates/node-fetch.ts +0 -198
package/core/generate.js
CHANGED
|
@@ -89,9 +89,6 @@ async function generate(options = {}, internalOptions) {
|
|
|
89
89
|
const runReactQuery = config.reactQuery ?? false;
|
|
90
90
|
const runCli = internalOptions?.skipCli ? false : !!config.cli;
|
|
91
91
|
const runOrm = runReactQuery || !!config.cli || (options.orm !== undefined ? !!options.orm : false);
|
|
92
|
-
// Auto-enable nodeHttpAdapter when CLI is enabled, unless explicitly set to false
|
|
93
|
-
const useNodeHttpAdapter = options.nodeHttpAdapter === true ||
|
|
94
|
-
(runCli && options.nodeHttpAdapter !== false);
|
|
95
92
|
const schemaEnabled = !!options.schema?.enabled;
|
|
96
93
|
if (!schemaEnabled && !runReactQuery && !runOrm && !runCli) {
|
|
97
94
|
return {
|
|
@@ -226,22 +223,13 @@ async function generate(options = {}, internalOptions) {
|
|
|
226
223
|
mutations: customOperations.mutations,
|
|
227
224
|
typeRegistry: customOperations.typeRegistry,
|
|
228
225
|
},
|
|
229
|
-
config
|
|
226
|
+
config,
|
|
230
227
|
sharedTypesPath: bothEnabled ? '..' : undefined,
|
|
231
228
|
});
|
|
232
229
|
filesToWrite.push(...files.map((file) => ({
|
|
233
230
|
...file,
|
|
234
231
|
path: node_path_1.default.posix.join('orm', file.path),
|
|
235
232
|
})));
|
|
236
|
-
// Generate NodeHttpAdapter in ORM output when enabled
|
|
237
|
-
if (useNodeHttpAdapter) {
|
|
238
|
-
const { generateNodeFetchFile } = await Promise.resolve().then(() => __importStar(require('./codegen/cli/utils-generator')));
|
|
239
|
-
const nodeFetchFile = generateNodeFetchFile();
|
|
240
|
-
filesToWrite.push({
|
|
241
|
-
path: node_path_1.default.posix.join('orm', nodeFetchFile.fileName),
|
|
242
|
-
content: nodeFetchFile.content,
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
233
|
}
|
|
246
234
|
// Generate CLI commands
|
|
247
235
|
if (runCli) {
|
|
@@ -252,7 +240,7 @@ async function generate(options = {}, internalOptions) {
|
|
|
252
240
|
queries: customOperations.queries,
|
|
253
241
|
mutations: customOperations.mutations,
|
|
254
242
|
},
|
|
255
|
-
config
|
|
243
|
+
config,
|
|
256
244
|
typeRegistry: customOperations.typeRegistry,
|
|
257
245
|
});
|
|
258
246
|
filesToWrite.push(...files.map((file) => ({
|
|
@@ -582,16 +570,11 @@ async function generateMulti(options) {
|
|
|
582
570
|
if (useUnifiedCli && cliTargets.length > 0 && !dryRun) {
|
|
583
571
|
const cliConfig = typeof unifiedCli === 'object' ? unifiedCli : {};
|
|
584
572
|
const toolName = cliConfig.toolName ?? 'app';
|
|
585
|
-
// Auto-enable nodeHttpAdapter for unified CLI unless explicitly disabled
|
|
586
|
-
// Check first target config for explicit nodeHttpAdapter setting
|
|
587
573
|
const firstTargetConfig = configs[names[0]];
|
|
588
|
-
const multiNodeHttpAdapter = firstTargetConfig?.nodeHttpAdapter === true ||
|
|
589
|
-
(firstTargetConfig?.nodeHttpAdapter !== false);
|
|
590
574
|
const { files } = (0, cli_1.generateMultiTargetCli)({
|
|
591
575
|
toolName,
|
|
592
576
|
builtinNames: cliConfig.builtinNames,
|
|
593
577
|
targets: cliTargets,
|
|
594
|
-
nodeHttpAdapter: multiNodeHttpAdapter,
|
|
595
578
|
entryPoint: cliConfig.entryPoint,
|
|
596
579
|
});
|
|
597
580
|
const cliFilesToWrite = files.map((file) => ({
|
|
@@ -7,9 +7,5 @@ export interface MultiTargetExecutorInput {
|
|
|
7
7
|
endpoint: string;
|
|
8
8
|
ormImportPath: string;
|
|
9
9
|
}
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
nodeHttpAdapter?: boolean;
|
|
13
|
-
}
|
|
14
|
-
export declare function generateExecutorFile(toolName: string, options?: ExecutorOptions): GeneratedFile;
|
|
15
|
-
export declare function generateMultiTargetExecutorFile(toolName: string, targets: MultiTargetExecutorInput[], options?: ExecutorOptions): GeneratedFile;
|
|
10
|
+
export declare function generateExecutorFile(toolName: string): GeneratedFile;
|
|
11
|
+
export declare function generateMultiTargetExecutorFile(toolName: string, targets: MultiTargetExecutorInput[]): GeneratedFile;
|
|
@@ -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);
|
|
@@ -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 {
|
|
@@ -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
|
*
|
|
@@ -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
|
]));
|