@constructive-io/graphql-codegen 4.9.0 → 4.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/core/codegen/cli/arg-mapper.d.ts +1 -1
  2. package/core/codegen/cli/arg-mapper.js +15 -11
  3. package/core/codegen/cli/command-map-generator.d.ts +1 -0
  4. package/core/codegen/cli/command-map-generator.js +4 -0
  5. package/core/codegen/cli/config-command-generator.d.ts +11 -0
  6. package/core/codegen/cli/config-command-generator.js +458 -0
  7. package/core/codegen/cli/custom-command-generator.js +4 -3
  8. package/core/codegen/cli/docs-generator.d.ts +6 -5
  9. package/core/codegen/cli/docs-generator.js +167 -64
  10. package/core/codegen/cli/helpers-generator.d.ts +15 -0
  11. package/core/codegen/cli/helpers-generator.js +119 -0
  12. package/core/codegen/cli/index.d.ts +4 -0
  13. package/core/codegen/cli/index.js +21 -3
  14. package/core/codegen/cli/table-command-generator.d.ts +15 -0
  15. package/core/codegen/cli/table-command-generator.js +20 -1
  16. package/core/codegen/docs-utils.d.ts +26 -1
  17. package/core/codegen/docs-utils.js +105 -0
  18. package/core/codegen/orm/index.js +3 -2
  19. package/core/codegen/orm/input-types-generator.d.ts +3 -1
  20. package/core/codegen/orm/input-types-generator.js +123 -17
  21. package/core/codegen/orm/model-generator.d.ts +6 -2
  22. package/core/codegen/orm/model-generator.js +59 -29
  23. package/core/codegen/orm/select-types.d.ts +4 -2
  24. package/core/codegen/scalars.js +8 -0
  25. package/core/codegen/templates/cli-entry.ts +2 -2
  26. package/core/codegen/templates/cli-utils.ts +28 -0
  27. package/core/codegen/templates/query-builder.ts +28 -5
  28. package/core/codegen/templates/select-types.ts +4 -2
  29. package/core/generate.js +14 -4
  30. package/esm/core/codegen/cli/arg-mapper.d.ts +1 -1
  31. package/esm/core/codegen/cli/arg-mapper.js +15 -11
  32. package/esm/core/codegen/cli/command-map-generator.d.ts +1 -0
  33. package/esm/core/codegen/cli/command-map-generator.js +4 -0
  34. package/esm/core/codegen/cli/config-command-generator.d.ts +11 -0
  35. package/esm/core/codegen/cli/config-command-generator.js +422 -0
  36. package/esm/core/codegen/cli/custom-command-generator.js +4 -3
  37. package/esm/core/codegen/cli/docs-generator.d.ts +6 -5
  38. package/esm/core/codegen/cli/docs-generator.js +168 -65
  39. package/esm/core/codegen/cli/helpers-generator.d.ts +15 -0
  40. package/esm/core/codegen/cli/helpers-generator.js +83 -0
  41. package/esm/core/codegen/cli/index.d.ts +4 -0
  42. package/esm/core/codegen/cli/index.js +18 -2
  43. package/esm/core/codegen/cli/table-command-generator.d.ts +15 -0
  44. package/esm/core/codegen/cli/table-command-generator.js +20 -3
  45. package/esm/core/codegen/docs-utils.d.ts +26 -1
  46. package/esm/core/codegen/docs-utils.js +102 -0
  47. package/esm/core/codegen/orm/index.js +3 -2
  48. package/esm/core/codegen/orm/input-types-generator.d.ts +3 -1
  49. package/esm/core/codegen/orm/input-types-generator.js +123 -17
  50. package/esm/core/codegen/orm/model-generator.d.ts +6 -2
  51. package/esm/core/codegen/orm/model-generator.js +59 -29
  52. package/esm/core/codegen/orm/select-types.d.ts +4 -2
  53. package/esm/core/codegen/scalars.js +8 -0
  54. package/esm/core/generate.js +14 -4
  55. package/esm/types/config.d.ts +9 -0
  56. package/esm/types/config.js +1 -0
  57. package/package.json +11 -11
  58. package/types/config.d.ts +9 -0
  59. package/types/config.js +1 -0
@@ -1,4 +1,4 @@
1
1
  import * as t from '@babel/types';
2
2
  import type { CleanArgument } from '../../../types/schema';
3
- export declare function buildQuestionObject(arg: CleanArgument): t.ObjectExpression;
3
+ export declare function buildQuestionObject(arg: CleanArgument, namePrefix?: string): t.ObjectExpression;
4
4
  export declare function buildQuestionsArray(args: CleanArgument[]): t.ArrayExpression;
@@ -11,35 +11,37 @@ function resolveBaseType(typeRef) {
11
11
  }
12
12
  return typeRef;
13
13
  }
14
- export function buildQuestionObject(arg) {
14
+ export function buildQuestionObject(arg, namePrefix) {
15
15
  const { inner, required } = unwrapNonNull(arg.type);
16
16
  const base = resolveBaseType(arg.type);
17
17
  const props = [];
18
+ const questionName = namePrefix ? `${namePrefix}.${arg.name}` : arg.name;
18
19
  if (base.kind === 'ENUM' && base.enumValues && base.enumValues.length > 0) {
19
20
  props.push(t.objectProperty(t.identifier('type'), t.stringLiteral('autocomplete')));
20
- props.push(t.objectProperty(t.identifier('name'), t.stringLiteral(arg.name)));
21
- props.push(t.objectProperty(t.identifier('message'), t.stringLiteral(arg.description || arg.name)));
21
+ props.push(t.objectProperty(t.identifier('name'), t.stringLiteral(questionName)));
22
+ props.push(t.objectProperty(t.identifier('message'), t.stringLiteral(arg.description || questionName)));
22
23
  props.push(t.objectProperty(t.identifier('options'), t.arrayExpression(base.enumValues.map((v) => t.stringLiteral(v)))));
23
24
  }
24
25
  else if (base.kind === 'SCALAR' && base.name === 'Boolean') {
25
26
  props.push(t.objectProperty(t.identifier('type'), t.stringLiteral('confirm')));
26
- props.push(t.objectProperty(t.identifier('name'), t.stringLiteral(arg.name)));
27
- props.push(t.objectProperty(t.identifier('message'), t.stringLiteral(arg.description || arg.name)));
27
+ props.push(t.objectProperty(t.identifier('name'), t.stringLiteral(questionName)));
28
+ props.push(t.objectProperty(t.identifier('message'), t.stringLiteral(arg.description || questionName)));
28
29
  props.push(t.objectProperty(t.identifier('default'), t.booleanLiteral(false)));
29
30
  }
30
31
  else if (base.kind === 'SCALAR' &&
31
32
  (base.name === 'Int' || base.name === 'Float')) {
32
33
  props.push(t.objectProperty(t.identifier('type'), t.stringLiteral('text')));
33
- props.push(t.objectProperty(t.identifier('name'), t.stringLiteral(arg.name)));
34
- props.push(t.objectProperty(t.identifier('message'), t.stringLiteral(arg.description || `${arg.name} (number)`)));
34
+ props.push(t.objectProperty(t.identifier('name'), t.stringLiteral(questionName)));
35
+ props.push(t.objectProperty(t.identifier('message'), t.stringLiteral(arg.description || `${questionName} (number)`)));
35
36
  }
36
37
  else if (inner.kind === 'INPUT_OBJECT' && inner.inputFields) {
38
+ // INPUT_OBJECT fields are flattened in buildQuestionsArray with dot-notation
37
39
  return buildInputObjectQuestion(arg.name, inner, required);
38
40
  }
39
41
  else {
40
42
  props.push(t.objectProperty(t.identifier('type'), t.stringLiteral('text')));
41
- props.push(t.objectProperty(t.identifier('name'), t.stringLiteral(arg.name)));
42
- props.push(t.objectProperty(t.identifier('message'), t.stringLiteral(arg.description || arg.name)));
43
+ props.push(t.objectProperty(t.identifier('name'), t.stringLiteral(questionName)));
44
+ props.push(t.objectProperty(t.identifier('message'), t.stringLiteral(arg.description || questionName)));
43
45
  }
44
46
  if (required) {
45
47
  props.push(t.objectProperty(t.identifier('required'), t.booleanLiteral(true)));
@@ -63,13 +65,15 @@ export function buildQuestionsArray(args) {
63
65
  const base = resolveBaseType(arg.type);
64
66
  const { inner } = unwrapNonNull(arg.type);
65
67
  if (inner.kind === 'INPUT_OBJECT' && inner.inputFields) {
68
+ // Flatten INPUT_OBJECT fields with dot-notation: e.g. input.email, input.password
66
69
  for (const field of inner.inputFields) {
67
- questions.push(buildQuestionObject(field));
70
+ questions.push(buildQuestionObject(field, arg.name));
68
71
  }
69
72
  }
70
73
  else if (base.kind === 'INPUT_OBJECT' && base.inputFields) {
74
+ // Same for NON_NULL-wrapped INPUT_OBJECT
71
75
  for (const field of base.inputFields) {
72
- questions.push(buildQuestionObject(field));
76
+ questions.push(buildQuestionObject(field, arg.name));
73
77
  }
74
78
  }
75
79
  else {
@@ -6,6 +6,7 @@ export interface MultiTargetCommandMapInput {
6
6
  builtinNames: {
7
7
  auth: string;
8
8
  context: string;
9
+ config: string;
9
10
  };
10
11
  targets: Array<{
11
12
  name: string;
@@ -186,6 +186,9 @@ export function generateMultiTargetCommandMap(input) {
186
186
  const authImportName = `${builtinNames.auth}Cmd`;
187
187
  commandEntries.push({ kebab: builtinNames.auth, importName: authImportName });
188
188
  statements.push(createImportDeclaration(`./commands/${builtinNames.auth}`, authImportName));
189
+ const configImportName = `${builtinNames.config}Cmd`;
190
+ commandEntries.push({ kebab: builtinNames.config, importName: configImportName });
191
+ statements.push(createImportDeclaration(`./commands/${builtinNames.config}`, configImportName));
189
192
  for (const target of targets) {
190
193
  for (const table of target.tables) {
191
194
  const { singularName } = getTableNames(table);
@@ -222,6 +225,7 @@ export function generateMultiTargetCommandMap(input) {
222
225
  'Commands:',
223
226
  ` ${builtinNames.context.padEnd(20)} Manage API contexts`,
224
227
  ` ${builtinNames.auth.padEnd(20)} Manage authentication`,
228
+ ` ${builtinNames.config.padEnd(20)} Manage config key-value store`,
225
229
  ];
226
230
  for (const target of targets) {
227
231
  usageLines.push('');
@@ -0,0 +1,11 @@
1
+ import type { GeneratedFile } from './executor-generator';
2
+ /**
3
+ * Generate the config command file (get/set/list/delete for per-context vars).
4
+ *
5
+ * Usage:
6
+ * <tool> config get <key>
7
+ * <tool> config set <key> <value>
8
+ * <tool> config list
9
+ * <tool> config delete <key>
10
+ */
11
+ export declare function generateConfigCommand(toolName: string, commandName: string): GeneratedFile;
@@ -0,0 +1,422 @@
1
+ import * as t from '@babel/types';
2
+ import { generateCode } from '../babel-ast';
3
+ import { getGeneratedFileHeader } from '../utils';
4
+ function createImportDeclaration(moduleSpecifier, namedImports, typeOnly = false) {
5
+ const specifiers = namedImports.map((name) => t.importSpecifier(t.identifier(name), t.identifier(name)));
6
+ const decl = t.importDeclaration(specifiers, t.stringLiteral(moduleSpecifier));
7
+ decl.importKind = typeOnly ? 'type' : 'value';
8
+ return decl;
9
+ }
10
+ function buildSwitchCase(testValue, handlerName, args) {
11
+ return t.switchCase(t.stringLiteral(testValue), [
12
+ t.returnStatement(t.callExpression(t.identifier(handlerName), args)),
13
+ ]);
14
+ }
15
+ function buildDefaultSwitchCase(usageVarName) {
16
+ return t.switchCase(null, [
17
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('log')), [t.identifier(usageVarName)])),
18
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('process'), t.identifier('exit')), [t.numericLiteral(1)])),
19
+ ]);
20
+ }
21
+ /**
22
+ * Generate the config command file (get/set/list/delete for per-context vars).
23
+ *
24
+ * Usage:
25
+ * <tool> config get <key>
26
+ * <tool> config set <key> <value>
27
+ * <tool> config list
28
+ * <tool> config delete <key>
29
+ */
30
+ export function generateConfigCommand(toolName, commandName) {
31
+ const statements = [];
32
+ statements.push(createImportDeclaration('inquirerer', [
33
+ 'CLIOptions',
34
+ 'Inquirerer',
35
+ 'extractFirst',
36
+ ]));
37
+ statements.push(createImportDeclaration('../executor', ['getStore']));
38
+ const usageStr = `
39
+ ${toolName} ${commandName} <command>
40
+
41
+ Commands:
42
+ get <key> Get a config value
43
+ set <key> <value> Set a config value
44
+ list List all config values
45
+ delete <key> Delete a config value
46
+
47
+ --help, -h Show this help message
48
+ `;
49
+ statements.push(t.variableDeclaration('const', [
50
+ t.variableDeclarator(t.identifier('usage'), t.stringLiteral(usageStr)),
51
+ ]));
52
+ // Main export: default async (argv, prompter, _options) => { ... }
53
+ const argvParam = t.identifier('argv');
54
+ argvParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Partial'), t.tsTypeParameterInstantiation([
55
+ t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
56
+ t.tsStringKeyword(),
57
+ t.tsUnknownKeyword(),
58
+ ])),
59
+ ])));
60
+ const prompterParam = t.identifier('prompter');
61
+ prompterParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Inquirerer')));
62
+ const optionsParam = t.identifier('_options');
63
+ optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('CLIOptions')));
64
+ const mainBody = [
65
+ // Help check
66
+ t.ifStatement(t.logicalExpression('||', t.memberExpression(t.identifier('argv'), t.identifier('help')), t.memberExpression(t.identifier('argv'), t.identifier('h'))), t.blockStatement([
67
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('log')), [t.identifier('usage')])),
68
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('process'), t.identifier('exit')), [t.numericLiteral(0)])),
69
+ ])),
70
+ // const store = getStore();
71
+ t.variableDeclaration('const', [
72
+ t.variableDeclarator(t.identifier('store'), t.callExpression(t.identifier('getStore'), [])),
73
+ ]),
74
+ // const { first: subcommand, newArgv } = extractFirst(argv);
75
+ t.variableDeclaration('const', [
76
+ t.variableDeclarator(t.objectPattern([
77
+ t.objectProperty(t.identifier('first'), t.identifier('subcommand')),
78
+ t.objectProperty(t.identifier('newArgv'), t.identifier('newArgv'), false, true),
79
+ ]), t.callExpression(t.identifier('extractFirst'), [
80
+ t.identifier('argv'),
81
+ ])),
82
+ ]),
83
+ // If no subcommand, prompt
84
+ t.ifStatement(t.unaryExpression('!', t.identifier('subcommand')), t.blockStatement([
85
+ t.variableDeclaration('const', [
86
+ t.variableDeclarator(t.identifier('answer'), t.awaitExpression(t.callExpression(t.memberExpression(t.identifier('prompter'), t.identifier('prompt')), [
87
+ t.identifier('argv'),
88
+ t.arrayExpression([
89
+ t.objectExpression([
90
+ t.objectProperty(t.identifier('type'), t.stringLiteral('autocomplete')),
91
+ t.objectProperty(t.identifier('name'), t.stringLiteral('subcommand')),
92
+ t.objectProperty(t.identifier('message'), t.stringLiteral('What do you want to do?')),
93
+ t.objectProperty(t.identifier('options'), t.arrayExpression([
94
+ t.stringLiteral('get'),
95
+ t.stringLiteral('set'),
96
+ t.stringLiteral('list'),
97
+ t.stringLiteral('delete'),
98
+ ])),
99
+ ]),
100
+ ]),
101
+ ]))),
102
+ ]),
103
+ t.returnStatement(t.callExpression(t.identifier('handleSubcommand'), [
104
+ t.memberExpression(t.identifier('answer'), t.identifier('subcommand')),
105
+ t.identifier('newArgv'),
106
+ t.identifier('prompter'),
107
+ t.identifier('store'),
108
+ ])),
109
+ ])),
110
+ t.returnStatement(t.callExpression(t.identifier('handleSubcommand'), [
111
+ t.identifier('subcommand'),
112
+ t.identifier('newArgv'),
113
+ t.identifier('prompter'),
114
+ t.identifier('store'),
115
+ ])),
116
+ ];
117
+ const mainExport = t.exportDefaultDeclaration(t.arrowFunctionExpression([argvParam, prompterParam, optionsParam], t.blockStatement(mainBody), true));
118
+ statements.push(mainExport);
119
+ // handleSubcommand function
120
+ const subcmdParam = t.identifier('subcommand');
121
+ subcmdParam.typeAnnotation = t.tsTypeAnnotation(t.tsStringKeyword());
122
+ const argvParam2 = t.identifier('argv');
123
+ argvParam2.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Partial'), t.tsTypeParameterInstantiation([
124
+ t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
125
+ t.tsStringKeyword(),
126
+ t.tsUnknownKeyword(),
127
+ ])),
128
+ ])));
129
+ const prompterParam2 = t.identifier('prompter');
130
+ prompterParam2.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Inquirerer')));
131
+ const storeParam = t.identifier('store');
132
+ storeParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('ReturnType'), t.tsTypeParameterInstantiation([
133
+ t.tsTypeQuery(t.identifier('getStore')),
134
+ ])));
135
+ const handleSubcommandFunc = t.functionDeclaration(t.identifier('handleSubcommand'), [subcmdParam, argvParam2, prompterParam2, storeParam], t.blockStatement([
136
+ t.switchStatement(t.identifier('subcommand'), [
137
+ buildSwitchCase('get', 'handleGet', [
138
+ t.identifier('argv'),
139
+ t.identifier('prompter'),
140
+ t.identifier('store'),
141
+ ]),
142
+ buildSwitchCase('set', 'handleSet', [
143
+ t.identifier('argv'),
144
+ t.identifier('prompter'),
145
+ t.identifier('store'),
146
+ ]),
147
+ buildSwitchCase('list', 'handleList', [t.identifier('store')]),
148
+ buildSwitchCase('delete', 'handleDelete', [
149
+ t.identifier('argv'),
150
+ t.identifier('prompter'),
151
+ t.identifier('store'),
152
+ ]),
153
+ buildDefaultSwitchCase('usage'),
154
+ ]),
155
+ ]), false, true);
156
+ statements.push(handleSubcommandFunc);
157
+ // handleGet
158
+ statements.push(buildGetHandler());
159
+ // handleSet
160
+ statements.push(buildSetHandler());
161
+ // handleList
162
+ statements.push(buildListHandler());
163
+ // handleDelete
164
+ statements.push(buildDeleteHandler());
165
+ const header = getGeneratedFileHeader('Config key-value store commands');
166
+ const code = generateCode(statements);
167
+ return {
168
+ fileName: `commands/${commandName}.ts`,
169
+ content: header + '\n' + code,
170
+ };
171
+ }
172
+ function buildGetHandler() {
173
+ const argvParam = t.identifier('argv');
174
+ argvParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Partial'), t.tsTypeParameterInstantiation([
175
+ t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
176
+ t.tsStringKeyword(),
177
+ t.tsUnknownKeyword(),
178
+ ])),
179
+ ])));
180
+ const prompterParam = t.identifier('prompter');
181
+ prompterParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Inquirerer')));
182
+ const storeParam = t.identifier('store');
183
+ storeParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('ReturnType'), t.tsTypeParameterInstantiation([
184
+ t.tsTypeQuery(t.identifier('getStore')),
185
+ ])));
186
+ const body = [
187
+ // const { first: key } = extractFirst(argv);
188
+ t.variableDeclaration('const', [
189
+ t.variableDeclarator(t.objectPattern([
190
+ t.objectProperty(t.identifier('first'), t.identifier('key')),
191
+ ]), t.callExpression(t.identifier('extractFirst'), [
192
+ t.identifier('argv'),
193
+ ])),
194
+ ]),
195
+ // Prompt if no key
196
+ t.ifStatement(t.unaryExpression('!', t.identifier('key')), t.blockStatement([
197
+ t.variableDeclaration('const', [
198
+ t.variableDeclarator(t.identifier('answers'), t.awaitExpression(t.callExpression(t.memberExpression(t.identifier('prompter'), t.identifier('prompt')), [
199
+ t.identifier('argv'),
200
+ t.arrayExpression([
201
+ t.objectExpression([
202
+ t.objectProperty(t.identifier('type'), t.stringLiteral('text')),
203
+ t.objectProperty(t.identifier('name'), t.stringLiteral('key')),
204
+ t.objectProperty(t.identifier('message'), t.stringLiteral('Config key')),
205
+ t.objectProperty(t.identifier('required'), t.booleanLiteral(true)),
206
+ ]),
207
+ ]),
208
+ ]))),
209
+ ]),
210
+ t.variableDeclaration('const', [
211
+ t.variableDeclarator(t.identifier('value'), t.callExpression(t.memberExpression(t.identifier('store'), t.identifier('getVar')), [t.memberExpression(t.identifier('answers'), t.identifier('key'))])),
212
+ ]),
213
+ t.ifStatement(t.binaryExpression('===', t.identifier('value'), t.identifier('undefined')), t.blockStatement([
214
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('error')), [
215
+ t.templateLiteral([
216
+ t.templateElement({ raw: 'Key "', cooked: 'Key "' }),
217
+ t.templateElement({ raw: '" not found.', cooked: '" not found.' }, true),
218
+ ], [t.memberExpression(t.identifier('answers'), t.identifier('key'))]),
219
+ ])),
220
+ ]), t.blockStatement([
221
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('log')), [t.identifier('value')])),
222
+ ])),
223
+ t.returnStatement(),
224
+ ])),
225
+ // const value = store.getVar(key);
226
+ t.variableDeclaration('const', [
227
+ t.variableDeclarator(t.identifier('value'), t.callExpression(t.memberExpression(t.identifier('store'), t.identifier('getVar')), [t.identifier('key')])),
228
+ ]),
229
+ t.ifStatement(t.binaryExpression('===', t.identifier('value'), t.identifier('undefined')), t.blockStatement([
230
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('error')), [
231
+ t.templateLiteral([
232
+ t.templateElement({ raw: 'Key "', cooked: 'Key "' }),
233
+ t.templateElement({ raw: '" not found.', cooked: '" not found.' }, true),
234
+ ], [t.identifier('key')]),
235
+ ])),
236
+ ]), t.blockStatement([
237
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('log')), [t.identifier('value')])),
238
+ ])),
239
+ ];
240
+ return t.functionDeclaration(t.identifier('handleGet'), [argvParam, prompterParam, storeParam], t.blockStatement(body), false, true);
241
+ }
242
+ function buildSetHandler() {
243
+ const argvParam = t.identifier('argv');
244
+ argvParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Partial'), t.tsTypeParameterInstantiation([
245
+ t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
246
+ t.tsStringKeyword(),
247
+ t.tsUnknownKeyword(),
248
+ ])),
249
+ ])));
250
+ const prompterParam = t.identifier('prompter');
251
+ prompterParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Inquirerer')));
252
+ const storeParam = t.identifier('store');
253
+ storeParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('ReturnType'), t.tsTypeParameterInstantiation([
254
+ t.tsTypeQuery(t.identifier('getStore')),
255
+ ])));
256
+ const body = [
257
+ // const { first: key, newArgv: restArgv } = extractFirst(argv);
258
+ t.variableDeclaration('const', [
259
+ t.variableDeclarator(t.objectPattern([
260
+ t.objectProperty(t.identifier('first'), t.identifier('key')),
261
+ t.objectProperty(t.identifier('newArgv'), t.identifier('restArgv')),
262
+ ]), t.callExpression(t.identifier('extractFirst'), [
263
+ t.identifier('argv'),
264
+ ])),
265
+ ]),
266
+ // const { first: value } = extractFirst(restArgv);
267
+ t.variableDeclaration('const', [
268
+ t.variableDeclarator(t.objectPattern([
269
+ t.objectProperty(t.identifier('first'), t.identifier('value')),
270
+ ]), t.callExpression(t.identifier('extractFirst'), [
271
+ t.identifier('restArgv'),
272
+ ])),
273
+ ]),
274
+ // Prompt if missing key or value
275
+ t.ifStatement(t.logicalExpression('||', t.unaryExpression('!', t.identifier('key')), t.unaryExpression('!', t.identifier('value'))), t.blockStatement([
276
+ t.variableDeclaration('const', [
277
+ t.variableDeclarator(t.identifier('answers'), t.awaitExpression(t.callExpression(t.memberExpression(t.identifier('prompter'), t.identifier('prompt')), [
278
+ t.objectExpression([
279
+ t.objectProperty(t.identifier('key'), t.identifier('key'), false, true),
280
+ t.objectProperty(t.identifier('value'), t.identifier('value'), false, true),
281
+ ]),
282
+ t.arrayExpression([
283
+ t.objectExpression([
284
+ t.objectProperty(t.identifier('type'), t.stringLiteral('text')),
285
+ t.objectProperty(t.identifier('name'), t.stringLiteral('key')),
286
+ t.objectProperty(t.identifier('message'), t.stringLiteral('Config key')),
287
+ t.objectProperty(t.identifier('required'), t.booleanLiteral(true)),
288
+ ]),
289
+ t.objectExpression([
290
+ t.objectProperty(t.identifier('type'), t.stringLiteral('text')),
291
+ t.objectProperty(t.identifier('name'), t.stringLiteral('value')),
292
+ t.objectProperty(t.identifier('message'), t.stringLiteral('Config value')),
293
+ t.objectProperty(t.identifier('required'), t.booleanLiteral(true)),
294
+ ]),
295
+ ]),
296
+ ]))),
297
+ ]),
298
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('store'), t.identifier('setVar')), [
299
+ t.memberExpression(t.identifier('answers'), t.identifier('key')),
300
+ t.callExpression(t.memberExpression(t.identifier('String'), t.identifier('call')), [
301
+ t.identifier('undefined'),
302
+ t.memberExpression(t.identifier('answers'), t.identifier('value')),
303
+ ]),
304
+ ])),
305
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('log')), [
306
+ t.templateLiteral([
307
+ t.templateElement({ raw: 'Set ', cooked: 'Set ' }),
308
+ t.templateElement({ raw: ' = ', cooked: ' = ' }),
309
+ t.templateElement({ raw: '', cooked: '' }, true),
310
+ ], [
311
+ t.memberExpression(t.identifier('answers'), t.identifier('key')),
312
+ t.memberExpression(t.identifier('answers'), t.identifier('value')),
313
+ ]),
314
+ ])),
315
+ t.returnStatement(),
316
+ ])),
317
+ // store.setVar(key, String(value));
318
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('store'), t.identifier('setVar')), [
319
+ t.identifier('key'),
320
+ t.callExpression(t.identifier('String'), [t.identifier('value')]),
321
+ ])),
322
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('log')), [
323
+ t.templateLiteral([
324
+ t.templateElement({ raw: 'Set ', cooked: 'Set ' }),
325
+ t.templateElement({ raw: ' = ', cooked: ' = ' }),
326
+ t.templateElement({ raw: '', cooked: '' }, true),
327
+ ], [t.identifier('key'), t.identifier('value')]),
328
+ ])),
329
+ ];
330
+ return t.functionDeclaration(t.identifier('handleSet'), [argvParam, prompterParam, storeParam], t.blockStatement(body), false, true);
331
+ }
332
+ function buildListHandler() {
333
+ const storeParam = t.identifier('store');
334
+ storeParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('ReturnType'), t.tsTypeParameterInstantiation([
335
+ t.tsTypeQuery(t.identifier('getStore')),
336
+ ])));
337
+ const body = [
338
+ // const vars = store.listVars();
339
+ t.variableDeclaration('const', [
340
+ t.variableDeclarator(t.identifier('vars'), t.callExpression(t.memberExpression(t.identifier('store'), t.identifier('listVars')), [])),
341
+ ]),
342
+ // const entries = Object.entries(vars);
343
+ t.variableDeclaration('const', [
344
+ t.variableDeclarator(t.identifier('entries'), t.callExpression(t.memberExpression(t.identifier('Object'), t.identifier('entries')), [t.identifier('vars')])),
345
+ ]),
346
+ t.ifStatement(t.binaryExpression('===', t.memberExpression(t.identifier('entries'), t.identifier('length')), t.numericLiteral(0)), t.blockStatement([
347
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('log')), [t.stringLiteral('No config values set.')])),
348
+ t.returnStatement(),
349
+ ])),
350
+ // for (const [key, value] of entries) { console.log(`${key} = ${value}`); }
351
+ t.forOfStatement(t.variableDeclaration('const', [
352
+ t.variableDeclarator(t.arrayPattern([t.identifier('key'), t.identifier('value')])),
353
+ ]), t.identifier('entries'), t.blockStatement([
354
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('log')), [
355
+ t.templateLiteral([
356
+ t.templateElement({ raw: '', cooked: '' }),
357
+ t.templateElement({ raw: ' = ', cooked: ' = ' }),
358
+ t.templateElement({ raw: '', cooked: '' }, true),
359
+ ], [t.identifier('key'), t.identifier('value')]),
360
+ ])),
361
+ ])),
362
+ ];
363
+ return t.functionDeclaration(t.identifier('handleList'), [storeParam], t.blockStatement(body));
364
+ }
365
+ function buildDeleteHandler() {
366
+ const argvParam = t.identifier('argv');
367
+ argvParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Partial'), t.tsTypeParameterInstantiation([
368
+ t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
369
+ t.tsStringKeyword(),
370
+ t.tsUnknownKeyword(),
371
+ ])),
372
+ ])));
373
+ const prompterParam = t.identifier('prompter');
374
+ prompterParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Inquirerer')));
375
+ const storeParam = t.identifier('store');
376
+ storeParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('ReturnType'), t.tsTypeParameterInstantiation([
377
+ t.tsTypeQuery(t.identifier('getStore')),
378
+ ])));
379
+ const body = [
380
+ // const { first: key } = extractFirst(argv);
381
+ t.variableDeclaration('const', [
382
+ t.variableDeclarator(t.objectPattern([
383
+ t.objectProperty(t.identifier('first'), t.identifier('key')),
384
+ ]), t.callExpression(t.identifier('extractFirst'), [
385
+ t.identifier('argv'),
386
+ ])),
387
+ ]),
388
+ // Prompt if no key
389
+ t.ifStatement(t.unaryExpression('!', t.identifier('key')), t.blockStatement([
390
+ t.variableDeclaration('const', [
391
+ t.variableDeclarator(t.identifier('answers'), t.awaitExpression(t.callExpression(t.memberExpression(t.identifier('prompter'), t.identifier('prompt')), [
392
+ t.identifier('argv'),
393
+ t.arrayExpression([
394
+ t.objectExpression([
395
+ t.objectProperty(t.identifier('type'), t.stringLiteral('text')),
396
+ t.objectProperty(t.identifier('name'), t.stringLiteral('key')),
397
+ t.objectProperty(t.identifier('message'), t.stringLiteral('Config key to delete')),
398
+ t.objectProperty(t.identifier('required'), t.booleanLiteral(true)),
399
+ ]),
400
+ ]),
401
+ ]))),
402
+ ]),
403
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('store'), t.identifier('deleteVar')), [t.memberExpression(t.identifier('answers'), t.identifier('key'))])),
404
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('log')), [
405
+ t.templateLiteral([
406
+ t.templateElement({ raw: 'Deleted key: ', cooked: 'Deleted key: ' }),
407
+ t.templateElement({ raw: '', cooked: '' }, true),
408
+ ], [t.memberExpression(t.identifier('answers'), t.identifier('key'))]),
409
+ ])),
410
+ t.returnStatement(),
411
+ ])),
412
+ // store.deleteVar(key);
413
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('store'), t.identifier('deleteVar')), [t.identifier('key')])),
414
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('log')), [
415
+ t.templateLiteral([
416
+ t.templateElement({ raw: 'Deleted key: ', cooked: 'Deleted key: ' }),
417
+ t.templateElement({ raw: '', cooked: '' }, true),
418
+ ], [t.identifier('key')]),
419
+ ])),
420
+ ];
421
+ return t.functionDeclaration(t.identifier('handleDelete'), [argvParam, prompterParam, storeParam], t.blockStatement(body), false, true);
422
+ }
@@ -119,7 +119,7 @@ export function generateCustomCommand(op, options) {
119
119
  // Build the list of utils imports needed
120
120
  const utilsImports = [];
121
121
  if (hasInputObjectArg) {
122
- utilsImports.push('parseMutationInput');
122
+ utilsImports.push('unflattenDotNotation');
123
123
  }
124
124
  if (isObjectReturn) {
125
125
  utilsImports.push('buildSelectFromPaths');
@@ -168,10 +168,11 @@ export function generateCustomCommand(op, options) {
168
168
  t.variableDeclarator(t.identifier('client'), t.callExpression(t.identifier('getClient'), getClientArgs)),
169
169
  ]));
170
170
  // For mutations with INPUT_OBJECT args (like `input: SignUpInput`),
171
- // parse JSON strings from CLI into proper objects
171
+ // reconstruct nested objects from dot-notation CLI answers.
172
+ // e.g. { 'input.email': 'foo', 'input.password': 'bar' } → { input: { email: 'foo', password: 'bar' } }
172
173
  if (hasInputObjectArg && op.args.length > 0) {
173
174
  bodyStatements.push(t.variableDeclaration('const', [
174
- t.variableDeclarator(t.identifier('parsedAnswers'), t.callExpression(t.identifier('parseMutationInput'), [
175
+ t.variableDeclarator(t.identifier('parsedAnswers'), t.callExpression(t.identifier('unflattenDotNotation'), [
175
176
  t.identifier('answers'),
176
177
  ])),
177
178
  ]));
@@ -1,17 +1,18 @@
1
- import type { CleanTable, CleanOperation } from '../../../types/schema';
1
+ import type { CleanTable, CleanOperation, TypeRegistry } from '../../../types/schema';
2
2
  import type { GeneratedDocFile, McpTool } from '../docs-utils';
3
3
  export { resolveDocsConfig } from '../docs-utils';
4
4
  export type { GeneratedDocFile, McpTool } from '../docs-utils';
5
- export declare function generateReadme(tables: CleanTable[], customOperations: CleanOperation[], toolName: string): GeneratedDocFile;
6
- export declare function generateAgentsDocs(tables: CleanTable[], customOperations: CleanOperation[], toolName: string): GeneratedDocFile;
7
- export declare function getCliMcpTools(tables: CleanTable[], customOperations: CleanOperation[], toolName: string): McpTool[];
8
- export declare function generateSkills(tables: CleanTable[], customOperations: CleanOperation[], toolName: string, targetName: string): GeneratedDocFile[];
5
+ export declare function generateReadme(tables: CleanTable[], customOperations: CleanOperation[], toolName: string, registry?: TypeRegistry): GeneratedDocFile;
6
+ export declare function generateAgentsDocs(tables: CleanTable[], customOperations: CleanOperation[], toolName: string, registry?: TypeRegistry): GeneratedDocFile;
7
+ export declare function getCliMcpTools(tables: CleanTable[], customOperations: CleanOperation[], toolName: string, registry?: TypeRegistry): McpTool[];
8
+ export declare function generateSkills(tables: CleanTable[], customOperations: CleanOperation[], toolName: string, targetName: string, registry?: TypeRegistry): GeneratedDocFile[];
9
9
  export interface MultiTargetDocsInput {
10
10
  toolName: string;
11
11
  builtinNames: {
12
12
  auth: string;
13
13
  context: string;
14
14
  };
15
+ registry?: TypeRegistry;
15
16
  targets: Array<{
16
17
  name: string;
17
18
  endpoint: string;