@constructive-io/graphql-codegen 4.16.0 → 4.17.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.
@@ -544,8 +544,8 @@ function generateSkills(tables, customOperations, toolName, targetName, registry
544
544
  const editableFields = (0, docs_utils_1.getEditableFields)(table, registry);
545
545
  const defaultFields = (0, table_command_generator_1.getFieldsWithDefaults)(table, registry);
546
546
  const createFlags = [
547
- ...editableFields.filter((f) => !defaultFields.has(f.name)).map((f) => `--${f.name} <value>`),
548
- ...editableFields.filter((f) => defaultFields.has(f.name)).map((f) => `[--${f.name} <value>]`),
547
+ ...editableFields.filter((f) => !defaultFields.has(f.name)).map((f) => `--${f.name} <${(0, docs_utils_1.cleanTypeName)(f.type.gqlType)}>`),
548
+ ...editableFields.filter((f) => defaultFields.has(f.name)).map((f) => `[--${f.name} <${(0, docs_utils_1.cleanTypeName)(f.type.gqlType)}>]`),
549
549
  ].join(' ');
550
550
  referenceNames.push(kebab);
551
551
  const skillSpecialGroups = (0, docs_utils_1.categorizeSpecialFields)(table, registry);
@@ -560,10 +560,10 @@ function generateSkills(tables, customOperations, toolName, targetName, registry
560
560
  description: skillSpecialDesc,
561
561
  usage: [
562
562
  `${toolName} ${kebab} list`,
563
- `${toolName} ${kebab} get --${pk.name} <value>`,
563
+ `${toolName} ${kebab} get --${pk.name} <${(0, docs_utils_1.cleanTypeName)(pk.gqlType)}>`,
564
564
  `${toolName} ${kebab} create ${createFlags}`,
565
- `${toolName} ${kebab} update --${pk.name} <value> ${editableFields.map((f) => `[--${f.name} <value>]`).join(' ')}`,
566
- `${toolName} ${kebab} delete --${pk.name} <value>`,
565
+ `${toolName} ${kebab} update --${pk.name} <${(0, docs_utils_1.cleanTypeName)(pk.gqlType)}> ${editableFields.map((f) => `[--${f.name} <${(0, docs_utils_1.cleanTypeName)(f.type.gqlType)}>]`).join(' ')}`,
566
+ `${toolName} ${kebab} delete --${pk.name} <${(0, docs_utils_1.cleanTypeName)(pk.gqlType)}>`,
567
567
  ],
568
568
  examples: [
569
569
  {
@@ -1345,8 +1345,8 @@ function generateMultiTargetSkills(input) {
1345
1345
  const editableFields = (0, docs_utils_1.getEditableFields)(table, registry);
1346
1346
  const defaultFields = (0, table_command_generator_1.getFieldsWithDefaults)(table, registry);
1347
1347
  const createFlags = [
1348
- ...editableFields.filter((f) => !defaultFields.has(f.name)).map((f) => `--${f.name} <value>`),
1349
- ...editableFields.filter((f) => defaultFields.has(f.name)).map((f) => `[--${f.name} <value>]`),
1348
+ ...editableFields.filter((f) => !defaultFields.has(f.name)).map((f) => `--${f.name} <${(0, docs_utils_1.cleanTypeName)(f.type.gqlType)}>`),
1349
+ ...editableFields.filter((f) => defaultFields.has(f.name)).map((f) => `[--${f.name} <${(0, docs_utils_1.cleanTypeName)(f.type.gqlType)}>]`),
1350
1350
  ].join(' ');
1351
1351
  const cmd = `${tgt.name}:${kebab}`;
1352
1352
  tgtReferenceNames.push(kebab);
@@ -1362,10 +1362,10 @@ function generateMultiTargetSkills(input) {
1362
1362
  description: mtSkillSpecialDesc,
1363
1363
  usage: [
1364
1364
  `${toolName} ${cmd} list`,
1365
- `${toolName} ${cmd} get --${pk.name} <value>`,
1365
+ `${toolName} ${cmd} get --${pk.name} <${(0, docs_utils_1.cleanTypeName)(pk.gqlType)}>`,
1366
1366
  `${toolName} ${cmd} create ${createFlags}`,
1367
- `${toolName} ${cmd} update --${pk.name} <value> ${editableFields.map((f) => `[--${f.name} <value>]`).join(' ')}`,
1368
- `${toolName} ${cmd} delete --${pk.name} <value>`,
1367
+ `${toolName} ${cmd} update --${pk.name} <${(0, docs_utils_1.cleanTypeName)(pk.gqlType)}> ${editableFields.map((f) => `[--${f.name} <${(0, docs_utils_1.cleanTypeName)(f.type.gqlType)}>]`).join(' ')}`,
1368
+ `${toolName} ${cmd} delete --${pk.name} <${(0, docs_utils_1.cleanTypeName)(pk.gqlType)}>`,
1369
1369
  ],
1370
1370
  examples: [
1371
1371
  {
@@ -93,9 +93,31 @@ export declare function cleanTypeName(name: string): string;
93
93
  export declare function flattenArgs(args: CleanArgument[], registry?: TypeRegistry): FlattenedArg[];
94
94
  /**
95
95
  * Build CLI flags string from flattened args.
96
- * e.g. '--input.email <value> --input.password <value>'
96
+ * e.g. '--input.email <String> --input.password <String>'
97
97
  */
98
98
  export declare function flattenedArgsToFlags(flatArgs: FlattenedArg[]): string;
99
+ /**
100
+ * Generate a type-aware placeholder for a table field value in code examples.
101
+ * Returns a quoted placeholder string, e.g. `'<UUID>'`, `'<String>'`, `'<Int>'`.
102
+ */
103
+ export declare function fieldPlaceholder(field: CleanField): string;
104
+ /**
105
+ * Generate a type-aware placeholder for a primary key value in code examples.
106
+ * PrimaryKeyField has `gqlType` directly (not nested under `.type`).
107
+ */
108
+ export declare function pkPlaceholder(pk: {
109
+ gqlType: string;
110
+ }): string;
111
+ /**
112
+ * Generate a type-aware placeholder for an operation argument value.
113
+ *
114
+ * - Scalar args: `'<String>'`
115
+ * - INPUT_OBJECT with resolvable fields (up to a limit): `{ email: '<String>', password: '<String>' }`
116
+ * - INPUT_OBJECT without resolvable fields: `'<TypeName>'`
117
+ *
118
+ * The result is a ready-to-embed JS expression (quotes included for scalars).
119
+ */
120
+ export declare function argPlaceholder(arg: CleanArgument, registry?: TypeRegistry): string;
99
121
  export declare function gqlTypeToJsonSchemaType(gqlType: string): string;
100
122
  export declare function buildSkillFile(skill: SkillDefinition, referenceNames?: string[]): string;
101
123
  export declare function buildSkillReference(ref: SkillReferenceDefinition): string;
@@ -13,6 +13,9 @@ exports.buildSpecialFieldsPlain = buildSpecialFieldsPlain;
13
13
  exports.cleanTypeName = cleanTypeName;
14
14
  exports.flattenArgs = flattenArgs;
15
15
  exports.flattenedArgsToFlags = flattenedArgsToFlags;
16
+ exports.fieldPlaceholder = fieldPlaceholder;
17
+ exports.pkPlaceholder = pkPlaceholder;
18
+ exports.argPlaceholder = argPlaceholder;
16
19
  exports.gqlTypeToJsonSchemaType = gqlTypeToJsonSchemaType;
17
20
  exports.buildSkillFile = buildSkillFile;
18
21
  exports.buildSkillReference = buildSkillReference;
@@ -343,10 +346,57 @@ function flattenArgs(args, registry) {
343
346
  }
344
347
  /**
345
348
  * Build CLI flags string from flattened args.
346
- * e.g. '--input.email <value> --input.password <value>'
349
+ * e.g. '--input.email <String> --input.password <String>'
347
350
  */
348
351
  function flattenedArgsToFlags(flatArgs) {
349
- return flatArgs.map((a) => `--${a.flag} <value>`).join(' ');
352
+ return flatArgs.map((a) => `--${a.flag} <${a.type}>`).join(' ');
353
+ }
354
+ // ---------------------------------------------------------------------------
355
+ // Type-aware placeholder helpers for code examples
356
+ // ---------------------------------------------------------------------------
357
+ /**
358
+ * Generate a type-aware placeholder for a table field value in code examples.
359
+ * Returns a quoted placeholder string, e.g. `'<UUID>'`, `'<String>'`, `'<Int>'`.
360
+ */
361
+ function fieldPlaceholder(field) {
362
+ return `'<${cleanTypeName(field.type.gqlType)}>'`;
363
+ }
364
+ /**
365
+ * Generate a type-aware placeholder for a primary key value in code examples.
366
+ * PrimaryKeyField has `gqlType` directly (not nested under `.type`).
367
+ */
368
+ function pkPlaceholder(pk) {
369
+ return `'<${cleanTypeName(pk.gqlType)}>'`;
370
+ }
371
+ /**
372
+ * Generate a type-aware placeholder for an operation argument value.
373
+ *
374
+ * - Scalar args: `'<String>'`
375
+ * - INPUT_OBJECT with resolvable fields (up to a limit): `{ email: '<String>', password: '<String>' }`
376
+ * - INPUT_OBJECT without resolvable fields: `'<TypeName>'`
377
+ *
378
+ * The result is a ready-to-embed JS expression (quotes included for scalars).
379
+ */
380
+ function argPlaceholder(arg, registry) {
381
+ const { inner } = unwrapNonNull(arg.type);
382
+ if (inner.kind === 'INPUT_OBJECT') {
383
+ const fields = resolveInputFields(inner, registry);
384
+ if (fields && fields.length > 0) {
385
+ const meaningful = fields.filter((f) => f.name !== 'clientMutationId');
386
+ if (meaningful.length > 0 && meaningful.length <= 5) {
387
+ const parts = meaningful.map((f) => {
388
+ const typeName = cleanTypeName(getScalarTypeName(f.type));
389
+ return `${f.name}: '<${typeName}>'`;
390
+ });
391
+ return '{ ' + parts.join(', ') + ' }';
392
+ }
393
+ }
394
+ if (inner.name) {
395
+ return `'<${cleanTypeName(inner.name)}>'`;
396
+ }
397
+ }
398
+ const typeName = cleanTypeName(getScalarTypeName(arg.type));
399
+ return `'<${typeName}>'`;
350
400
  }
351
401
  function gqlTypeToJsonSchemaType(gqlType) {
352
402
  switch (gqlType) {
@@ -1,6 +1,6 @@
1
- import type { CleanOperation, CleanTable } from '../../types/schema';
1
+ import type { CleanOperation, CleanTable, TypeRegistry } from '../../types/schema';
2
2
  import type { GeneratedDocFile, McpTool } from './docs-utils';
3
- export declare function generateHooksReadme(tables: CleanTable[], customOperations: CleanOperation[]): GeneratedDocFile;
3
+ export declare function generateHooksReadme(tables: CleanTable[], customOperations: CleanOperation[], registry?: TypeRegistry): GeneratedDocFile;
4
4
  export declare function generateHooksAgentsDocs(tables: CleanTable[], customOperations: CleanOperation[]): GeneratedDocFile;
5
5
  export declare function getHooksMcpTools(tables: CleanTable[], customOperations: CleanOperation[]): McpTool[];
6
- export declare function generateHooksSkills(tables: CleanTable[], customOperations: CleanOperation[], targetName: string): GeneratedDocFile[];
6
+ export declare function generateHooksSkills(tables: CleanTable[], customOperations: CleanOperation[], targetName: string, registry?: TypeRegistry): GeneratedDocFile[];
@@ -13,7 +13,7 @@ function getCustomHookName(op) {
13
13
  }
14
14
  return `use${(0, utils_1.ucFirst)(op.name)}Mutation`;
15
15
  }
16
- function generateHooksReadme(tables, customOperations) {
16
+ function generateHooksReadme(tables, customOperations, registry) {
17
17
  const lines = [];
18
18
  lines.push(...(0, docs_utils_1.getReadmeHeader)('React Query Hooks'));
19
19
  lines.push('## Setup');
@@ -76,7 +76,7 @@ function generateHooksReadme(tables, customOperations) {
76
76
  if ((0, utils_1.hasValidPrimaryKey)(table)) {
77
77
  lines.push(`// Get one ${singularName}`);
78
78
  lines.push(`const { data: item } = ${(0, utils_1.getSingleQueryHookName)(table)}({`);
79
- lines.push(` ${pk.name}: '<value>',`);
79
+ lines.push(` ${pk.name}: ${(0, docs_utils_1.pkPlaceholder)(pk)},`);
80
80
  lines.push(` selection: { fields: { ${scalarFields.map((f) => `${f.name}: true`).join(', ')} } },`);
81
81
  lines.push('});');
82
82
  lines.push('');
@@ -85,7 +85,7 @@ function generateHooksReadme(tables, customOperations) {
85
85
  lines.push(`const { mutate: create } = ${(0, utils_1.getCreateMutationHookName)(table)}({`);
86
86
  lines.push(` selection: { fields: { ${pk.name}: true } },`);
87
87
  lines.push('});');
88
- lines.push(`create({ ${scalarFields.filter((f) => f.name !== pk.name && f.name !== 'nodeId' && f.name !== 'createdAt' && f.name !== 'updatedAt').map((f) => `${f.name}: '<value>'`).join(', ')} });`);
88
+ lines.push(`create({ ${scalarFields.filter((f) => f.name !== pk.name && f.name !== 'nodeId' && f.name !== 'createdAt' && f.name !== 'updatedAt').map((f) => `${f.name}: ${(0, docs_utils_1.fieldPlaceholder)(f)}`).join(', ')} });`);
89
89
  lines.push('```');
90
90
  lines.push('');
91
91
  }
@@ -279,7 +279,7 @@ function getHooksMcpTools(tables, customOperations) {
279
279
  }
280
280
  return tools;
281
281
  }
282
- function generateHooksSkills(tables, customOperations, targetName) {
282
+ function generateHooksSkills(tables, customOperations, targetName, registry) {
283
283
  const files = [];
284
284
  const skillName = `hooks-${targetName}`;
285
285
  const referenceNames = [];
@@ -303,7 +303,7 @@ function generateHooksSkills(tables, customOperations, targetName) {
303
303
  `${(0, utils_1.getListQueryHookName)(table)}({ selection: { fields: { ${selectFields} } } })`,
304
304
  ...((0, utils_1.hasValidPrimaryKey)(table)
305
305
  ? [
306
- `${(0, utils_1.getSingleQueryHookName)(table)}({ ${pk.name}: '<value>', selection: { fields: { ${selectFields} } } })`,
306
+ `${(0, utils_1.getSingleQueryHookName)(table)}({ ${pk.name}: ${(0, docs_utils_1.pkPlaceholder)(pk)}, selection: { fields: { ${selectFields} } } })`,
307
307
  ]
308
308
  : []),
309
309
  `${(0, utils_1.getCreateMutationHookName)(table)}({ selection: { fields: { ${pk.name}: true } } })`,
@@ -329,7 +329,7 @@ function generateHooksSkills(tables, customOperations, targetName) {
329
329
  `const { mutate } = ${(0, utils_1.getCreateMutationHookName)(table)}({`,
330
330
  ` selection: { fields: { ${pk.name}: true } },`,
331
331
  '});',
332
- `mutate({ ${scalarFields.filter((f) => f.name !== pk.name && f.name !== 'nodeId' && f.name !== 'createdAt' && f.name !== 'updatedAt').map((f) => `${f.name}: '<value>'`).join(', ')} });`,
332
+ `mutate({ ${scalarFields.filter((f) => f.name !== pk.name && f.name !== 'nodeId' && f.name !== 'createdAt' && f.name !== 'updatedAt').map((f) => `${f.name}: ${(0, docs_utils_1.fieldPlaceholder)(f)}`).join(', ')} });`,
333
333
  ],
334
334
  },
335
335
  ],
@@ -340,7 +340,7 @@ function generateHooksSkills(tables, customOperations, targetName) {
340
340
  for (const op of customOperations) {
341
341
  const hookName = getCustomHookName(op);
342
342
  const callArgs = op.args.length > 0
343
- ? `{ ${op.args.map((a) => `${a.name}: '<value>'`).join(', ')} }`
343
+ ? `{ ${op.args.map((a) => `${a.name}: ${(0, docs_utils_1.argPlaceholder)(a, registry)}`).join(', ')} }`
344
344
  : '';
345
345
  const refName = (0, komoji_1.toKebabCase)(op.name);
346
346
  referenceNames.push(refName);
@@ -1,6 +1,6 @@
1
- import type { CleanOperation, CleanTable } from '../../../types/schema';
1
+ import type { CleanOperation, CleanTable, TypeRegistry } from '../../../types/schema';
2
2
  import type { GeneratedDocFile, McpTool } from '../docs-utils';
3
- export declare function generateOrmReadme(tables: CleanTable[], customOperations: CleanOperation[]): GeneratedDocFile;
3
+ export declare function generateOrmReadme(tables: CleanTable[], customOperations: CleanOperation[], registry?: TypeRegistry): GeneratedDocFile;
4
4
  export declare function generateOrmAgentsDocs(tables: CleanTable[], customOperations: CleanOperation[]): GeneratedDocFile;
5
5
  export declare function getOrmMcpTools(tables: CleanTable[], customOperations: CleanOperation[]): McpTool[];
6
- export declare function generateOrmSkills(tables: CleanTable[], customOperations: CleanOperation[], targetName: string): GeneratedDocFile[];
6
+ export declare function generateOrmSkills(tables: CleanTable[], customOperations: CleanOperation[], targetName: string, registry?: TypeRegistry): GeneratedDocFile[];
@@ -7,7 +7,7 @@ exports.generateOrmSkills = generateOrmSkills;
7
7
  const komoji_1 = require("komoji");
8
8
  const docs_utils_1 = require("../docs-utils");
9
9
  const utils_1 = require("../utils");
10
- function generateOrmReadme(tables, customOperations) {
10
+ function generateOrmReadme(tables, customOperations, registry) {
11
11
  const lines = [];
12
12
  lines.push(...(0, docs_utils_1.getReadmeHeader)('ORM Client'));
13
13
  lines.push('## Setup');
@@ -58,16 +58,16 @@ function generateOrmReadme(tables, customOperations) {
58
58
  lines.push(`const items = await db.${singularName}.findMany({ select: { ${scalarFields.map((f) => `${f.name}: true`).join(', ')} } }).execute();`);
59
59
  lines.push('');
60
60
  lines.push(`// Get one by ${pk.name}`);
61
- lines.push(`const item = await db.${singularName}.findOne({ ${pk.name}: '<value>', select: { ${scalarFields.map((f) => `${f.name}: true`).join(', ')} } }).execute();`);
61
+ lines.push(`const item = await db.${singularName}.findOne({ ${pk.name}: ${(0, docs_utils_1.pkPlaceholder)(pk)}, select: { ${scalarFields.map((f) => `${f.name}: true`).join(', ')} } }).execute();`);
62
62
  lines.push('');
63
63
  lines.push(`// Create`);
64
- lines.push(`const created = await db.${singularName}.create({ data: { ${editableFields.map((f) => `${f.name}: '<value>'`).join(', ')} }, select: { ${pk.name}: true } }).execute();`);
64
+ lines.push(`const created = await db.${singularName}.create({ data: { ${editableFields.map((f) => `${f.name}: ${(0, docs_utils_1.fieldPlaceholder)(f)}`).join(', ')} }, select: { ${pk.name}: true } }).execute();`);
65
65
  lines.push('');
66
66
  lines.push(`// Update`);
67
- lines.push(`const updated = await db.${singularName}.update({ where: { ${pk.name}: '<value>' }, data: { ${editableFields[0]?.name || 'field'}: '<new-value>' }, select: { ${pk.name}: true } }).execute();`);
67
+ lines.push(`const updated = await db.${singularName}.update({ where: { ${pk.name}: ${(0, docs_utils_1.pkPlaceholder)(pk)} }, data: { ${editableFields[0]?.name || 'field'}: ${editableFields[0] ? (0, docs_utils_1.fieldPlaceholder)(editableFields[0]) : "'<String>'"} }, select: { ${pk.name}: true } }).execute();`);
68
68
  lines.push('');
69
69
  lines.push(`// Delete`);
70
- lines.push(`const deleted = await db.${singularName}.delete({ where: { ${pk.name}: '<value>' } }).execute();`);
70
+ lines.push(`const deleted = await db.${singularName}.delete({ where: { ${pk.name}: ${(0, docs_utils_1.pkPlaceholder)(pk)} } }).execute();`);
71
71
  lines.push('```');
72
72
  lines.push('');
73
73
  const ormSpecialGroups = (0, docs_utils_1.categorizeSpecialFields)(table);
@@ -94,7 +94,7 @@ function generateOrmReadme(tables, customOperations) {
94
94
  }
95
95
  lines.push('');
96
96
  lines.push('```typescript');
97
- lines.push(`const result = await db.${accessor}.${op.name}({ ${op.args.map((a) => `${a.name}: '<value>'`).join(', ')} }).execute();`);
97
+ lines.push(`const result = await db.${accessor}.${op.name}({ ${op.args.map((a) => `${a.name}: ${(0, docs_utils_1.argPlaceholder)(a, registry)}`).join(', ')} }).execute();`);
98
98
  lines.push('```');
99
99
  }
100
100
  else {
@@ -285,7 +285,7 @@ function getOrmMcpTools(tables, customOperations) {
285
285
  }
286
286
  return tools;
287
287
  }
288
- function generateOrmSkills(tables, customOperations, targetName) {
288
+ function generateOrmSkills(tables, customOperations, targetName, registry) {
289
289
  const files = [];
290
290
  const skillName = `orm-${targetName}`;
291
291
  const referenceNames = [];
@@ -311,10 +311,10 @@ function generateOrmSkills(tables, customOperations, targetName) {
311
311
  language: 'typescript',
312
312
  usage: [
313
313
  `db.${modelName}.findMany({ select: { id: true } }).execute()`,
314
- `db.${modelName}.findOne({ ${pk.name}: '<value>', select: { id: true } }).execute()`,
315
- `db.${modelName}.create({ data: { ${editableFields.map((f) => `${f.name}: '<value>'`).join(', ')} }, select: { id: true } }).execute()`,
316
- `db.${modelName}.update({ where: { ${pk.name}: '<value>' }, data: { ${editableFields[0]?.name || 'field'}: '<new>' }, select: { id: true } }).execute()`,
317
- `db.${modelName}.delete({ where: { ${pk.name}: '<value>' } }).execute()`,
314
+ `db.${modelName}.findOne({ ${pk.name}: ${(0, docs_utils_1.pkPlaceholder)(pk)}, select: { id: true } }).execute()`,
315
+ `db.${modelName}.create({ data: { ${editableFields.map((f) => `${f.name}: ${(0, docs_utils_1.fieldPlaceholder)(f)}`).join(', ')} }, select: { id: true } }).execute()`,
316
+ `db.${modelName}.update({ where: { ${pk.name}: ${(0, docs_utils_1.pkPlaceholder)(pk)} }, data: { ${editableFields[0]?.name || 'field'}: ${editableFields[0] ? (0, docs_utils_1.fieldPlaceholder)(editableFields[0]) : "'<String>'"} }, select: { id: true } }).execute()`,
317
+ `db.${modelName}.delete({ where: { ${pk.name}: ${(0, docs_utils_1.pkPlaceholder)(pk)} } }).execute()`,
318
318
  ],
319
319
  examples: [
320
320
  {
@@ -329,7 +329,7 @@ function generateOrmSkills(tables, customOperations, targetName) {
329
329
  description: `Create a ${singularName}`,
330
330
  code: [
331
331
  `const item = await db.${modelName}.create({`,
332
- ` data: { ${editableFields.map((f) => `${f.name}: 'value'`).join(', ')} },`,
332
+ ` data: { ${editableFields.map((f) => `${f.name}: ${(0, docs_utils_1.fieldPlaceholder)(f)}`).join(', ')} },`,
333
333
  ` select: { ${pk.name}: true }`,
334
334
  '}).execute();',
335
335
  ],
@@ -342,7 +342,7 @@ function generateOrmSkills(tables, customOperations, targetName) {
342
342
  for (const op of customOperations) {
343
343
  const accessor = op.kind === 'query' ? 'query' : 'mutation';
344
344
  const callArgs = op.args.length > 0
345
- ? `{ ${op.args.map((a) => `${a.name}: '<value>'`).join(', ')} }`
345
+ ? `{ ${op.args.map((a) => `${a.name}: ${(0, docs_utils_1.argPlaceholder)(a, registry)}`).join(', ')} }`
346
346
  : '';
347
347
  const refName = (0, komoji_1.toKebabCase)(op.name);
348
348
  referenceNames.push(refName);
@@ -376,10 +376,10 @@ function generateOrmSkills(tables, customOperations, targetName) {
376
376
  '',
377
377
  `// Available models: ${tableNames.slice(0, 8).join(', ')}${tableNames.length > 8 ? ', ...' : ''}`,
378
378
  `db.<model>.findMany({ select: { id: true } }).execute()`,
379
- `db.<model>.findOne({ id: '<value>', select: { id: true } }).execute()`,
379
+ `db.<model>.findOne({ id: '<UUID>', select: { id: true } }).execute()`,
380
380
  `db.<model>.create({ data: { ... }, select: { id: true } }).execute()`,
381
- `db.<model>.update({ where: { id: '<value>' }, data: { ... }, select: { id: true } }).execute()`,
382
- `db.<model>.delete({ where: { id: '<value>' } }).execute()`,
381
+ `db.<model>.update({ where: { id: '<UUID>' }, data: { ... }, select: { id: true } }).execute()`,
382
+ `db.<model>.delete({ where: { id: '<UUID>' } }).execute()`,
383
383
  ],
384
384
  examples: [
385
385
  {
@@ -137,6 +137,55 @@ function generateEnumTypes(typeRegistry, tableTypeNames, comments = true) {
137
137
  }
138
138
  return { statements, generatedTypes };
139
139
  }
140
+ /**
141
+ * Generate a discriminated union type for a @oneOf INPUT_OBJECT.
142
+ *
143
+ * For a @oneOf input like:
144
+ * input BlueprintNodeInput @oneOf {
145
+ * shorthand: String
146
+ * DataId: DataIdParams
147
+ * DataTimestamps: DataTimestampsParams
148
+ * }
149
+ *
150
+ * Generates:
151
+ * type BlueprintNodeInput =
152
+ * | { shorthand: string }
153
+ * | { DataId: DataIdParams }
154
+ * | { DataTimestamps: DataTimestampsParams }
155
+ */
156
+ function generateOneOfUnionType(typeName, typeInfo, typeRegistry, tableTypeNames, generatedTypes, typesToGenerate, comments) {
157
+ const unionMembers = [];
158
+ if (typeInfo.inputFields && typeInfo.inputFields.length > 0) {
159
+ for (const field of typeInfo.inputFields) {
160
+ const tsType = typeRefToTs(field.type);
161
+ // Each @oneOf field becomes a single-property object type in the union
162
+ const prop = t.tsPropertySignature(t.identifier(field.name), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(tsType))));
163
+ prop.optional = false; // In @oneOf, the chosen field is required
164
+ const memberType = t.tsTypeLiteral([prop]);
165
+ unionMembers.push(memberType);
166
+ // Track nested types for generation
167
+ const baseType = (0, type_resolver_1.getTypeBaseName)(field.type);
168
+ if (baseType &&
169
+ !generatedTypes.has(baseType) &&
170
+ !shouldSkipType(baseType, tableTypeNames)) {
171
+ const nestedType = typeRegistry.get(baseType);
172
+ if (nestedType?.kind === 'INPUT_OBJECT') {
173
+ typesToGenerate.add(baseType);
174
+ }
175
+ }
176
+ }
177
+ }
178
+ const unionType = unionMembers.length > 0
179
+ ? t.tsUnionType(unionMembers)
180
+ : t.tsNeverKeyword();
181
+ const typeAlias = t.tsTypeAliasDeclaration(t.identifier(typeName), null, unionType);
182
+ const exportDecl = t.exportNamedDeclaration(typeAlias);
183
+ const inputDescription = (0, utils_1.stripSmartComments)(typeInfo.description, comments);
184
+ if (inputDescription) {
185
+ (0, babel_ast_1.addJSDocComment)(exportDecl, inputDescription.split('\n'));
186
+ }
187
+ return exportDecl;
188
+ }
140
189
  function generateInputObjectTypes(typeRegistry, tableTypeNames, alreadyGenerated, comments = true) {
141
190
  const statements = [];
142
191
  const generatedTypes = new Set(alreadyGenerated);
@@ -162,6 +211,11 @@ function generateInputObjectTypes(typeRegistry, tableTypeNames, alreadyGenerated
162
211
  if (!typeInfo || typeInfo.kind !== 'INPUT_OBJECT')
163
212
  continue;
164
213
  generatedTypes.add(typeName);
214
+ // @oneOf types become discriminated unions instead of interfaces
215
+ if (typeInfo.isOneOf) {
216
+ statements.push(generateOneOfUnionType(typeName, typeInfo, typeRegistry, tableTypeNames, generatedTypes, typesToGenerate, comments));
217
+ continue;
218
+ }
165
219
  const properties = [];
166
220
  if (typeInfo.inputFields && typeInfo.inputFields.length > 0) {
167
221
  for (const field of typeInfo.inputFields) {
package/core/generate.js CHANGED
@@ -279,7 +279,7 @@ async function generate(options = {}, internalOptions) {
279
279
  const skillsToWrite = [];
280
280
  if (runOrm) {
281
281
  if (docsConfig.readme) {
282
- const readme = (0, docs_generator_2.generateOrmReadme)(tables, allCustomOps);
282
+ const readme = (0, docs_generator_2.generateOrmReadme)(tables, allCustomOps, customOperations.typeRegistry);
283
283
  filesToWrite.push({ path: node_path_1.default.posix.join('orm', readme.fileName), content: readme.content });
284
284
  }
285
285
  if (docsConfig.agents) {
@@ -290,14 +290,14 @@ async function generate(options = {}, internalOptions) {
290
290
  allMcpTools.push(...(0, docs_generator_2.getOrmMcpTools)(tables, allCustomOps));
291
291
  }
292
292
  if (docsConfig.skills) {
293
- for (const skill of (0, docs_generator_2.generateOrmSkills)(tables, allCustomOps, targetName)) {
293
+ for (const skill of (0, docs_generator_2.generateOrmSkills)(tables, allCustomOps, targetName, customOperations.typeRegistry)) {
294
294
  skillsToWrite.push({ path: skill.fileName, content: skill.content });
295
295
  }
296
296
  }
297
297
  }
298
298
  if (runReactQuery) {
299
299
  if (docsConfig.readme) {
300
- const readme = (0, hooks_docs_generator_1.generateHooksReadme)(tables, allCustomOps);
300
+ const readme = (0, hooks_docs_generator_1.generateHooksReadme)(tables, allCustomOps, customOperations.typeRegistry);
301
301
  filesToWrite.push({ path: node_path_1.default.posix.join('hooks', readme.fileName), content: readme.content });
302
302
  }
303
303
  if (docsConfig.agents) {
@@ -308,7 +308,7 @@ async function generate(options = {}, internalOptions) {
308
308
  allMcpTools.push(...(0, hooks_docs_generator_1.getHooksMcpTools)(tables, allCustomOps));
309
309
  }
310
310
  if (docsConfig.skills) {
311
- for (const skill of (0, hooks_docs_generator_1.generateHooksSkills)(tables, allCustomOps, targetName)) {
311
+ for (const skill of (0, hooks_docs_generator_1.generateHooksSkills)(tables, allCustomOps, targetName, customOperations.typeRegistry)) {
312
312
  skillsToWrite.push({ path: skill.fileName, content: skill.content });
313
313
  }
314
314
  }
@@ -532,8 +532,8 @@ export function generateSkills(tables, customOperations, toolName, targetName, r
532
532
  const editableFields = getEditableFields(table, registry);
533
533
  const defaultFields = getFieldsWithDefaults(table, registry);
534
534
  const createFlags = [
535
- ...editableFields.filter((f) => !defaultFields.has(f.name)).map((f) => `--${f.name} <value>`),
536
- ...editableFields.filter((f) => defaultFields.has(f.name)).map((f) => `[--${f.name} <value>]`),
535
+ ...editableFields.filter((f) => !defaultFields.has(f.name)).map((f) => `--${f.name} <${cleanTypeName(f.type.gqlType)}>`),
536
+ ...editableFields.filter((f) => defaultFields.has(f.name)).map((f) => `[--${f.name} <${cleanTypeName(f.type.gqlType)}>]`),
537
537
  ].join(' ');
538
538
  referenceNames.push(kebab);
539
539
  const skillSpecialGroups = categorizeSpecialFields(table, registry);
@@ -548,10 +548,10 @@ export function generateSkills(tables, customOperations, toolName, targetName, r
548
548
  description: skillSpecialDesc,
549
549
  usage: [
550
550
  `${toolName} ${kebab} list`,
551
- `${toolName} ${kebab} get --${pk.name} <value>`,
551
+ `${toolName} ${kebab} get --${pk.name} <${cleanTypeName(pk.gqlType)}>`,
552
552
  `${toolName} ${kebab} create ${createFlags}`,
553
- `${toolName} ${kebab} update --${pk.name} <value> ${editableFields.map((f) => `[--${f.name} <value>]`).join(' ')}`,
554
- `${toolName} ${kebab} delete --${pk.name} <value>`,
553
+ `${toolName} ${kebab} update --${pk.name} <${cleanTypeName(pk.gqlType)}> ${editableFields.map((f) => `[--${f.name} <${cleanTypeName(f.type.gqlType)}>]`).join(' ')}`,
554
+ `${toolName} ${kebab} delete --${pk.name} <${cleanTypeName(pk.gqlType)}>`,
555
555
  ],
556
556
  examples: [
557
557
  {
@@ -1333,8 +1333,8 @@ export function generateMultiTargetSkills(input) {
1333
1333
  const editableFields = getEditableFields(table, registry);
1334
1334
  const defaultFields = getFieldsWithDefaults(table, registry);
1335
1335
  const createFlags = [
1336
- ...editableFields.filter((f) => !defaultFields.has(f.name)).map((f) => `--${f.name} <value>`),
1337
- ...editableFields.filter((f) => defaultFields.has(f.name)).map((f) => `[--${f.name} <value>]`),
1336
+ ...editableFields.filter((f) => !defaultFields.has(f.name)).map((f) => `--${f.name} <${cleanTypeName(f.type.gqlType)}>`),
1337
+ ...editableFields.filter((f) => defaultFields.has(f.name)).map((f) => `[--${f.name} <${cleanTypeName(f.type.gqlType)}>]`),
1338
1338
  ].join(' ');
1339
1339
  const cmd = `${tgt.name}:${kebab}`;
1340
1340
  tgtReferenceNames.push(kebab);
@@ -1350,10 +1350,10 @@ export function generateMultiTargetSkills(input) {
1350
1350
  description: mtSkillSpecialDesc,
1351
1351
  usage: [
1352
1352
  `${toolName} ${cmd} list`,
1353
- `${toolName} ${cmd} get --${pk.name} <value>`,
1353
+ `${toolName} ${cmd} get --${pk.name} <${cleanTypeName(pk.gqlType)}>`,
1354
1354
  `${toolName} ${cmd} create ${createFlags}`,
1355
- `${toolName} ${cmd} update --${pk.name} <value> ${editableFields.map((f) => `[--${f.name} <value>]`).join(' ')}`,
1356
- `${toolName} ${cmd} delete --${pk.name} <value>`,
1355
+ `${toolName} ${cmd} update --${pk.name} <${cleanTypeName(pk.gqlType)}> ${editableFields.map((f) => `[--${f.name} <${cleanTypeName(f.type.gqlType)}>]`).join(' ')}`,
1356
+ `${toolName} ${cmd} delete --${pk.name} <${cleanTypeName(pk.gqlType)}>`,
1357
1357
  ],
1358
1358
  examples: [
1359
1359
  {
@@ -93,9 +93,31 @@ export declare function cleanTypeName(name: string): string;
93
93
  export declare function flattenArgs(args: CleanArgument[], registry?: TypeRegistry): FlattenedArg[];
94
94
  /**
95
95
  * Build CLI flags string from flattened args.
96
- * e.g. '--input.email <value> --input.password <value>'
96
+ * e.g. '--input.email <String> --input.password <String>'
97
97
  */
98
98
  export declare function flattenedArgsToFlags(flatArgs: FlattenedArg[]): string;
99
+ /**
100
+ * Generate a type-aware placeholder for a table field value in code examples.
101
+ * Returns a quoted placeholder string, e.g. `'<UUID>'`, `'<String>'`, `'<Int>'`.
102
+ */
103
+ export declare function fieldPlaceholder(field: CleanField): string;
104
+ /**
105
+ * Generate a type-aware placeholder for a primary key value in code examples.
106
+ * PrimaryKeyField has `gqlType` directly (not nested under `.type`).
107
+ */
108
+ export declare function pkPlaceholder(pk: {
109
+ gqlType: string;
110
+ }): string;
111
+ /**
112
+ * Generate a type-aware placeholder for an operation argument value.
113
+ *
114
+ * - Scalar args: `'<String>'`
115
+ * - INPUT_OBJECT with resolvable fields (up to a limit): `{ email: '<String>', password: '<String>' }`
116
+ * - INPUT_OBJECT without resolvable fields: `'<TypeName>'`
117
+ *
118
+ * The result is a ready-to-embed JS expression (quotes included for scalars).
119
+ */
120
+ export declare function argPlaceholder(arg: CleanArgument, registry?: TypeRegistry): string;
99
121
  export declare function gqlTypeToJsonSchemaType(gqlType: string): string;
100
122
  export declare function buildSkillFile(skill: SkillDefinition, referenceNames?: string[]): string;
101
123
  export declare function buildSkillReference(ref: SkillReferenceDefinition): string;
@@ -325,10 +325,57 @@ export function flattenArgs(args, registry) {
325
325
  }
326
326
  /**
327
327
  * Build CLI flags string from flattened args.
328
- * e.g. '--input.email <value> --input.password <value>'
328
+ * e.g. '--input.email <String> --input.password <String>'
329
329
  */
330
330
  export function flattenedArgsToFlags(flatArgs) {
331
- return flatArgs.map((a) => `--${a.flag} <value>`).join(' ');
331
+ return flatArgs.map((a) => `--${a.flag} <${a.type}>`).join(' ');
332
+ }
333
+ // ---------------------------------------------------------------------------
334
+ // Type-aware placeholder helpers for code examples
335
+ // ---------------------------------------------------------------------------
336
+ /**
337
+ * Generate a type-aware placeholder for a table field value in code examples.
338
+ * Returns a quoted placeholder string, e.g. `'<UUID>'`, `'<String>'`, `'<Int>'`.
339
+ */
340
+ export function fieldPlaceholder(field) {
341
+ return `'<${cleanTypeName(field.type.gqlType)}>'`;
342
+ }
343
+ /**
344
+ * Generate a type-aware placeholder for a primary key value in code examples.
345
+ * PrimaryKeyField has `gqlType` directly (not nested under `.type`).
346
+ */
347
+ export function pkPlaceholder(pk) {
348
+ return `'<${cleanTypeName(pk.gqlType)}>'`;
349
+ }
350
+ /**
351
+ * Generate a type-aware placeholder for an operation argument value.
352
+ *
353
+ * - Scalar args: `'<String>'`
354
+ * - INPUT_OBJECT with resolvable fields (up to a limit): `{ email: '<String>', password: '<String>' }`
355
+ * - INPUT_OBJECT without resolvable fields: `'<TypeName>'`
356
+ *
357
+ * The result is a ready-to-embed JS expression (quotes included for scalars).
358
+ */
359
+ export function argPlaceholder(arg, registry) {
360
+ const { inner } = unwrapNonNull(arg.type);
361
+ if (inner.kind === 'INPUT_OBJECT') {
362
+ const fields = resolveInputFields(inner, registry);
363
+ if (fields && fields.length > 0) {
364
+ const meaningful = fields.filter((f) => f.name !== 'clientMutationId');
365
+ if (meaningful.length > 0 && meaningful.length <= 5) {
366
+ const parts = meaningful.map((f) => {
367
+ const typeName = cleanTypeName(getScalarTypeName(f.type));
368
+ return `${f.name}: '<${typeName}>'`;
369
+ });
370
+ return '{ ' + parts.join(', ') + ' }';
371
+ }
372
+ }
373
+ if (inner.name) {
374
+ return `'<${cleanTypeName(inner.name)}>'`;
375
+ }
376
+ }
377
+ const typeName = cleanTypeName(getScalarTypeName(arg.type));
378
+ return `'<${typeName}>'`;
332
379
  }
333
380
  export function gqlTypeToJsonSchemaType(gqlType) {
334
381
  switch (gqlType) {
@@ -1,6 +1,6 @@
1
- import type { CleanOperation, CleanTable } from '../../types/schema';
1
+ import type { CleanOperation, CleanTable, TypeRegistry } from '../../types/schema';
2
2
  import type { GeneratedDocFile, McpTool } from './docs-utils';
3
- export declare function generateHooksReadme(tables: CleanTable[], customOperations: CleanOperation[]): GeneratedDocFile;
3
+ export declare function generateHooksReadme(tables: CleanTable[], customOperations: CleanOperation[], registry?: TypeRegistry): GeneratedDocFile;
4
4
  export declare function generateHooksAgentsDocs(tables: CleanTable[], customOperations: CleanOperation[]): GeneratedDocFile;
5
5
  export declare function getHooksMcpTools(tables: CleanTable[], customOperations: CleanOperation[]): McpTool[];
6
- export declare function generateHooksSkills(tables: CleanTable[], customOperations: CleanOperation[], targetName: string): GeneratedDocFile[];
6
+ export declare function generateHooksSkills(tables: CleanTable[], customOperations: CleanOperation[], targetName: string, registry?: TypeRegistry): GeneratedDocFile[];
@@ -1,5 +1,5 @@
1
1
  import { toKebabCase } from 'komoji';
2
- import { buildSkillFile, buildSkillReference, formatArgType, getReadmeHeader, getReadmeFooter, gqlTypeToJsonSchemaType, } from './docs-utils';
2
+ import { buildSkillFile, buildSkillReference, formatArgType, fieldPlaceholder, pkPlaceholder, argPlaceholder, getReadmeHeader, getReadmeFooter, gqlTypeToJsonSchemaType, } from './docs-utils';
3
3
  import { getTableNames, getScalarFields, getPrimaryKeyInfo, getListQueryHookName, getSingleQueryHookName, getCreateMutationHookName, getUpdateMutationHookName, getDeleteMutationHookName, hasValidPrimaryKey, ucFirst, lcFirst, } from './utils';
4
4
  function getCustomHookName(op) {
5
5
  if (op.kind === 'query') {
@@ -7,7 +7,7 @@ function getCustomHookName(op) {
7
7
  }
8
8
  return `use${ucFirst(op.name)}Mutation`;
9
9
  }
10
- export function generateHooksReadme(tables, customOperations) {
10
+ export function generateHooksReadme(tables, customOperations, registry) {
11
11
  const lines = [];
12
12
  lines.push(...getReadmeHeader('React Query Hooks'));
13
13
  lines.push('## Setup');
@@ -70,7 +70,7 @@ export function generateHooksReadme(tables, customOperations) {
70
70
  if (hasValidPrimaryKey(table)) {
71
71
  lines.push(`// Get one ${singularName}`);
72
72
  lines.push(`const { data: item } = ${getSingleQueryHookName(table)}({`);
73
- lines.push(` ${pk.name}: '<value>',`);
73
+ lines.push(` ${pk.name}: ${pkPlaceholder(pk)},`);
74
74
  lines.push(` selection: { fields: { ${scalarFields.map((f) => `${f.name}: true`).join(', ')} } },`);
75
75
  lines.push('});');
76
76
  lines.push('');
@@ -79,7 +79,7 @@ export function generateHooksReadme(tables, customOperations) {
79
79
  lines.push(`const { mutate: create } = ${getCreateMutationHookName(table)}({`);
80
80
  lines.push(` selection: { fields: { ${pk.name}: true } },`);
81
81
  lines.push('});');
82
- lines.push(`create({ ${scalarFields.filter((f) => f.name !== pk.name && f.name !== 'nodeId' && f.name !== 'createdAt' && f.name !== 'updatedAt').map((f) => `${f.name}: '<value>'`).join(', ')} });`);
82
+ 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(', ')} });`);
83
83
  lines.push('```');
84
84
  lines.push('');
85
85
  }
@@ -273,7 +273,7 @@ export function getHooksMcpTools(tables, customOperations) {
273
273
  }
274
274
  return tools;
275
275
  }
276
- export function generateHooksSkills(tables, customOperations, targetName) {
276
+ export function generateHooksSkills(tables, customOperations, targetName, registry) {
277
277
  const files = [];
278
278
  const skillName = `hooks-${targetName}`;
279
279
  const referenceNames = [];
@@ -297,7 +297,7 @@ export function generateHooksSkills(tables, customOperations, targetName) {
297
297
  `${getListQueryHookName(table)}({ selection: { fields: { ${selectFields} } } })`,
298
298
  ...(hasValidPrimaryKey(table)
299
299
  ? [
300
- `${getSingleQueryHookName(table)}({ ${pk.name}: '<value>', selection: { fields: { ${selectFields} } } })`,
300
+ `${getSingleQueryHookName(table)}({ ${pk.name}: ${pkPlaceholder(pk)}, selection: { fields: { ${selectFields} } } })`,
301
301
  ]
302
302
  : []),
303
303
  `${getCreateMutationHookName(table)}({ selection: { fields: { ${pk.name}: true } } })`,
@@ -323,7 +323,7 @@ export function generateHooksSkills(tables, customOperations, targetName) {
323
323
  `const { mutate } = ${getCreateMutationHookName(table)}({`,
324
324
  ` selection: { fields: { ${pk.name}: true } },`,
325
325
  '});',
326
- `mutate({ ${scalarFields.filter((f) => f.name !== pk.name && f.name !== 'nodeId' && f.name !== 'createdAt' && f.name !== 'updatedAt').map((f) => `${f.name}: '<value>'`).join(', ')} });`,
326
+ `mutate({ ${scalarFields.filter((f) => f.name !== pk.name && f.name !== 'nodeId' && f.name !== 'createdAt' && f.name !== 'updatedAt').map((f) => `${f.name}: ${fieldPlaceholder(f)}`).join(', ')} });`,
327
327
  ],
328
328
  },
329
329
  ],
@@ -334,7 +334,7 @@ export function generateHooksSkills(tables, customOperations, targetName) {
334
334
  for (const op of customOperations) {
335
335
  const hookName = getCustomHookName(op);
336
336
  const callArgs = op.args.length > 0
337
- ? `{ ${op.args.map((a) => `${a.name}: '<value>'`).join(', ')} }`
337
+ ? `{ ${op.args.map((a) => `${a.name}: ${argPlaceholder(a, registry)}`).join(', ')} }`
338
338
  : '';
339
339
  const refName = toKebabCase(op.name);
340
340
  referenceNames.push(refName);
@@ -1,6 +1,6 @@
1
- import type { CleanOperation, CleanTable } from '../../../types/schema';
1
+ import type { CleanOperation, CleanTable, TypeRegistry } from '../../../types/schema';
2
2
  import type { GeneratedDocFile, McpTool } from '../docs-utils';
3
- export declare function generateOrmReadme(tables: CleanTable[], customOperations: CleanOperation[]): GeneratedDocFile;
3
+ export declare function generateOrmReadme(tables: CleanTable[], customOperations: CleanOperation[], registry?: TypeRegistry): GeneratedDocFile;
4
4
  export declare function generateOrmAgentsDocs(tables: CleanTable[], customOperations: CleanOperation[]): GeneratedDocFile;
5
5
  export declare function getOrmMcpTools(tables: CleanTable[], customOperations: CleanOperation[]): McpTool[];
6
- export declare function generateOrmSkills(tables: CleanTable[], customOperations: CleanOperation[], targetName: string): GeneratedDocFile[];
6
+ export declare function generateOrmSkills(tables: CleanTable[], customOperations: CleanOperation[], targetName: string, registry?: TypeRegistry): GeneratedDocFile[];
@@ -1,7 +1,7 @@
1
1
  import { toKebabCase } from 'komoji';
2
- import { buildSkillFile, buildSkillReference, formatArgType, getEditableFields, categorizeSpecialFields, buildSpecialFieldsMarkdown, getReadmeHeader, getReadmeFooter, gqlTypeToJsonSchemaType, } from '../docs-utils';
2
+ import { buildSkillFile, buildSkillReference, formatArgType, fieldPlaceholder, pkPlaceholder, argPlaceholder, getEditableFields, categorizeSpecialFields, buildSpecialFieldsMarkdown, getReadmeHeader, getReadmeFooter, gqlTypeToJsonSchemaType, } from '../docs-utils';
3
3
  import { getScalarFields, getTableNames, getPrimaryKeyInfo, lcFirst, } from '../utils';
4
- export function generateOrmReadme(tables, customOperations) {
4
+ export function generateOrmReadme(tables, customOperations, registry) {
5
5
  const lines = [];
6
6
  lines.push(...getReadmeHeader('ORM Client'));
7
7
  lines.push('## Setup');
@@ -52,16 +52,16 @@ export function generateOrmReadme(tables, customOperations) {
52
52
  lines.push(`const items = await db.${singularName}.findMany({ select: { ${scalarFields.map((f) => `${f.name}: true`).join(', ')} } }).execute();`);
53
53
  lines.push('');
54
54
  lines.push(`// Get one by ${pk.name}`);
55
- lines.push(`const item = await db.${singularName}.findOne({ ${pk.name}: '<value>', select: { ${scalarFields.map((f) => `${f.name}: true`).join(', ')} } }).execute();`);
55
+ lines.push(`const item = await db.${singularName}.findOne({ ${pk.name}: ${pkPlaceholder(pk)}, select: { ${scalarFields.map((f) => `${f.name}: true`).join(', ')} } }).execute();`);
56
56
  lines.push('');
57
57
  lines.push(`// Create`);
58
- lines.push(`const created = await db.${singularName}.create({ data: { ${editableFields.map((f) => `${f.name}: '<value>'`).join(', ')} }, select: { ${pk.name}: true } }).execute();`);
58
+ lines.push(`const created = await db.${singularName}.create({ data: { ${editableFields.map((f) => `${f.name}: ${fieldPlaceholder(f)}`).join(', ')} }, select: { ${pk.name}: true } }).execute();`);
59
59
  lines.push('');
60
60
  lines.push(`// Update`);
61
- lines.push(`const updated = await db.${singularName}.update({ where: { ${pk.name}: '<value>' }, data: { ${editableFields[0]?.name || 'field'}: '<new-value>' }, select: { ${pk.name}: true } }).execute();`);
61
+ lines.push(`const updated = await db.${singularName}.update({ where: { ${pk.name}: ${pkPlaceholder(pk)} }, data: { ${editableFields[0]?.name || 'field'}: ${editableFields[0] ? fieldPlaceholder(editableFields[0]) : "'<String>'"} }, select: { ${pk.name}: true } }).execute();`);
62
62
  lines.push('');
63
63
  lines.push(`// Delete`);
64
- lines.push(`const deleted = await db.${singularName}.delete({ where: { ${pk.name}: '<value>' } }).execute();`);
64
+ lines.push(`const deleted = await db.${singularName}.delete({ where: { ${pk.name}: ${pkPlaceholder(pk)} } }).execute();`);
65
65
  lines.push('```');
66
66
  lines.push('');
67
67
  const ormSpecialGroups = categorizeSpecialFields(table);
@@ -88,7 +88,7 @@ export function generateOrmReadme(tables, customOperations) {
88
88
  }
89
89
  lines.push('');
90
90
  lines.push('```typescript');
91
- lines.push(`const result = await db.${accessor}.${op.name}({ ${op.args.map((a) => `${a.name}: '<value>'`).join(', ')} }).execute();`);
91
+ lines.push(`const result = await db.${accessor}.${op.name}({ ${op.args.map((a) => `${a.name}: ${argPlaceholder(a, registry)}`).join(', ')} }).execute();`);
92
92
  lines.push('```');
93
93
  }
94
94
  else {
@@ -279,7 +279,7 @@ export function getOrmMcpTools(tables, customOperations) {
279
279
  }
280
280
  return tools;
281
281
  }
282
- export function generateOrmSkills(tables, customOperations, targetName) {
282
+ export function generateOrmSkills(tables, customOperations, targetName, registry) {
283
283
  const files = [];
284
284
  const skillName = `orm-${targetName}`;
285
285
  const referenceNames = [];
@@ -305,10 +305,10 @@ export function generateOrmSkills(tables, customOperations, targetName) {
305
305
  language: 'typescript',
306
306
  usage: [
307
307
  `db.${modelName}.findMany({ select: { id: true } }).execute()`,
308
- `db.${modelName}.findOne({ ${pk.name}: '<value>', select: { id: true } }).execute()`,
309
- `db.${modelName}.create({ data: { ${editableFields.map((f) => `${f.name}: '<value>'`).join(', ')} }, select: { id: true } }).execute()`,
310
- `db.${modelName}.update({ where: { ${pk.name}: '<value>' }, data: { ${editableFields[0]?.name || 'field'}: '<new>' }, select: { id: true } }).execute()`,
311
- `db.${modelName}.delete({ where: { ${pk.name}: '<value>' } }).execute()`,
308
+ `db.${modelName}.findOne({ ${pk.name}: ${pkPlaceholder(pk)}, select: { id: true } }).execute()`,
309
+ `db.${modelName}.create({ data: { ${editableFields.map((f) => `${f.name}: ${fieldPlaceholder(f)}`).join(', ')} }, select: { id: true } }).execute()`,
310
+ `db.${modelName}.update({ where: { ${pk.name}: ${pkPlaceholder(pk)} }, data: { ${editableFields[0]?.name || 'field'}: ${editableFields[0] ? fieldPlaceholder(editableFields[0]) : "'<String>'"} }, select: { id: true } }).execute()`,
311
+ `db.${modelName}.delete({ where: { ${pk.name}: ${pkPlaceholder(pk)} } }).execute()`,
312
312
  ],
313
313
  examples: [
314
314
  {
@@ -323,7 +323,7 @@ export function generateOrmSkills(tables, customOperations, targetName) {
323
323
  description: `Create a ${singularName}`,
324
324
  code: [
325
325
  `const item = await db.${modelName}.create({`,
326
- ` data: { ${editableFields.map((f) => `${f.name}: 'value'`).join(', ')} },`,
326
+ ` data: { ${editableFields.map((f) => `${f.name}: ${fieldPlaceholder(f)}`).join(', ')} },`,
327
327
  ` select: { ${pk.name}: true }`,
328
328
  '}).execute();',
329
329
  ],
@@ -336,7 +336,7 @@ export function generateOrmSkills(tables, customOperations, targetName) {
336
336
  for (const op of customOperations) {
337
337
  const accessor = op.kind === 'query' ? 'query' : 'mutation';
338
338
  const callArgs = op.args.length > 0
339
- ? `{ ${op.args.map((a) => `${a.name}: '<value>'`).join(', ')} }`
339
+ ? `{ ${op.args.map((a) => `${a.name}: ${argPlaceholder(a, registry)}`).join(', ')} }`
340
340
  : '';
341
341
  const refName = toKebabCase(op.name);
342
342
  referenceNames.push(refName);
@@ -370,10 +370,10 @@ export function generateOrmSkills(tables, customOperations, targetName) {
370
370
  '',
371
371
  `// Available models: ${tableNames.slice(0, 8).join(', ')}${tableNames.length > 8 ? ', ...' : ''}`,
372
372
  `db.<model>.findMany({ select: { id: true } }).execute()`,
373
- `db.<model>.findOne({ id: '<value>', select: { id: true } }).execute()`,
373
+ `db.<model>.findOne({ id: '<UUID>', select: { id: true } }).execute()`,
374
374
  `db.<model>.create({ data: { ... }, select: { id: true } }).execute()`,
375
- `db.<model>.update({ where: { id: '<value>' }, data: { ... }, select: { id: true } }).execute()`,
376
- `db.<model>.delete({ where: { id: '<value>' } }).execute()`,
375
+ `db.<model>.update({ where: { id: '<UUID>' }, data: { ... }, select: { id: true } }).execute()`,
376
+ `db.<model>.delete({ where: { id: '<UUID>' } }).execute()`,
377
377
  ],
378
378
  examples: [
379
379
  {
@@ -101,6 +101,55 @@ function generateEnumTypes(typeRegistry, tableTypeNames, comments = true) {
101
101
  }
102
102
  return { statements, generatedTypes };
103
103
  }
104
+ /**
105
+ * Generate a discriminated union type for a @oneOf INPUT_OBJECT.
106
+ *
107
+ * For a @oneOf input like:
108
+ * input BlueprintNodeInput @oneOf {
109
+ * shorthand: String
110
+ * DataId: DataIdParams
111
+ * DataTimestamps: DataTimestampsParams
112
+ * }
113
+ *
114
+ * Generates:
115
+ * type BlueprintNodeInput =
116
+ * | { shorthand: string }
117
+ * | { DataId: DataIdParams }
118
+ * | { DataTimestamps: DataTimestampsParams }
119
+ */
120
+ function generateOneOfUnionType(typeName, typeInfo, typeRegistry, tableTypeNames, generatedTypes, typesToGenerate, comments) {
121
+ const unionMembers = [];
122
+ if (typeInfo.inputFields && typeInfo.inputFields.length > 0) {
123
+ for (const field of typeInfo.inputFields) {
124
+ const tsType = typeRefToTs(field.type);
125
+ // Each @oneOf field becomes a single-property object type in the union
126
+ const prop = t.tsPropertySignature(t.identifier(field.name), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(tsType))));
127
+ prop.optional = false; // In @oneOf, the chosen field is required
128
+ const memberType = t.tsTypeLiteral([prop]);
129
+ unionMembers.push(memberType);
130
+ // Track nested types for generation
131
+ const baseType = getTypeBaseName(field.type);
132
+ if (baseType &&
133
+ !generatedTypes.has(baseType) &&
134
+ !shouldSkipType(baseType, tableTypeNames)) {
135
+ const nestedType = typeRegistry.get(baseType);
136
+ if (nestedType?.kind === 'INPUT_OBJECT') {
137
+ typesToGenerate.add(baseType);
138
+ }
139
+ }
140
+ }
141
+ }
142
+ const unionType = unionMembers.length > 0
143
+ ? t.tsUnionType(unionMembers)
144
+ : t.tsNeverKeyword();
145
+ const typeAlias = t.tsTypeAliasDeclaration(t.identifier(typeName), null, unionType);
146
+ const exportDecl = t.exportNamedDeclaration(typeAlias);
147
+ const inputDescription = stripSmartComments(typeInfo.description, comments);
148
+ if (inputDescription) {
149
+ addJSDocComment(exportDecl, inputDescription.split('\n'));
150
+ }
151
+ return exportDecl;
152
+ }
104
153
  function generateInputObjectTypes(typeRegistry, tableTypeNames, alreadyGenerated, comments = true) {
105
154
  const statements = [];
106
155
  const generatedTypes = new Set(alreadyGenerated);
@@ -126,6 +175,11 @@ function generateInputObjectTypes(typeRegistry, tableTypeNames, alreadyGenerated
126
175
  if (!typeInfo || typeInfo.kind !== 'INPUT_OBJECT')
127
176
  continue;
128
177
  generatedTypes.add(typeName);
178
+ // @oneOf types become discriminated unions instead of interfaces
179
+ if (typeInfo.isOneOf) {
180
+ statements.push(generateOneOfUnionType(typeName, typeInfo, typeRegistry, tableTypeNames, generatedTypes, typesToGenerate, comments));
181
+ continue;
182
+ }
129
183
  const properties = [];
130
184
  if (typeInfo.inputFields && typeInfo.inputFields.length > 0) {
131
185
  for (const field of typeInfo.inputFields) {
@@ -237,7 +237,7 @@ export async function generate(options = {}, internalOptions) {
237
237
  const skillsToWrite = [];
238
238
  if (runOrm) {
239
239
  if (docsConfig.readme) {
240
- const readme = generateOrmReadme(tables, allCustomOps);
240
+ const readme = generateOrmReadme(tables, allCustomOps, customOperations.typeRegistry);
241
241
  filesToWrite.push({ path: path.posix.join('orm', readme.fileName), content: readme.content });
242
242
  }
243
243
  if (docsConfig.agents) {
@@ -248,14 +248,14 @@ export async function generate(options = {}, internalOptions) {
248
248
  allMcpTools.push(...getOrmMcpTools(tables, allCustomOps));
249
249
  }
250
250
  if (docsConfig.skills) {
251
- for (const skill of generateOrmSkills(tables, allCustomOps, targetName)) {
251
+ for (const skill of generateOrmSkills(tables, allCustomOps, targetName, customOperations.typeRegistry)) {
252
252
  skillsToWrite.push({ path: skill.fileName, content: skill.content });
253
253
  }
254
254
  }
255
255
  }
256
256
  if (runReactQuery) {
257
257
  if (docsConfig.readme) {
258
- const readme = generateHooksReadme(tables, allCustomOps);
258
+ const readme = generateHooksReadme(tables, allCustomOps, customOperations.typeRegistry);
259
259
  filesToWrite.push({ path: path.posix.join('hooks', readme.fileName), content: readme.content });
260
260
  }
261
261
  if (docsConfig.agents) {
@@ -266,7 +266,7 @@ export async function generate(options = {}, internalOptions) {
266
266
  allMcpTools.push(...getHooksMcpTools(tables, allCustomOps));
267
267
  }
268
268
  if (docsConfig.skills) {
269
- for (const skill of generateHooksSkills(tables, allCustomOps, targetName)) {
269
+ for (const skill of generateHooksSkills(tables, allCustomOps, targetName, customOperations.typeRegistry)) {
270
270
  skillsToWrite.push({ path: skill.fileName, content: skill.content });
271
271
  }
272
272
  }
@@ -262,4 +262,6 @@ export interface ResolvedType {
262
262
  enumValues?: string[];
263
263
  /** Possible types for UNION types */
264
264
  possibleTypes?: string[];
265
+ /** Whether this INPUT_OBJECT uses @oneOf (GraphQL 16+ discriminated union input) */
266
+ isOneOf?: boolean;
265
267
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constructive-io/graphql-codegen",
3
- "version": "4.16.0",
3
+ "version": "4.17.0",
4
4
  "description": "GraphQL SDK generator for Constructive databases with React Query hooks",
5
5
  "keywords": [
6
6
  "graphql",
@@ -56,15 +56,15 @@
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.6.6",
59
+ "@constructive-io/graphql-query": "^3.7.0",
60
60
  "@constructive-io/graphql-types": "^3.3.4",
61
61
  "@inquirerer/utils": "^3.3.4",
62
- "@pgpmjs/core": "^6.7.1",
62
+ "@pgpmjs/core": "^6.8.0",
63
63
  "ajv": "^8.18.0",
64
64
  "deepmerge": "^4.3.1",
65
65
  "find-and-require-package-json": "^0.9.1",
66
66
  "gql-ast": "^3.3.3",
67
- "graphile-schema": "^1.6.6",
67
+ "graphile-schema": "^1.7.0",
68
68
  "graphql": "16.13.0",
69
69
  "inflekt": "^0.3.3",
70
70
  "inquirerer": "^4.7.0",
@@ -73,8 +73,8 @@
73
73
  "oxfmt": "^0.40.0",
74
74
  "pg-cache": "^3.3.4",
75
75
  "pg-env": "^1.7.3",
76
- "pgsql-client": "^3.5.7",
77
- "pgsql-seed": "^2.5.7",
76
+ "pgsql-client": "^3.5.8",
77
+ "pgsql-seed": "^2.5.8",
78
78
  "undici": "^7.24.3"
79
79
  },
80
80
  "peerDependencies": {
@@ -101,5 +101,5 @@
101
101
  "tsx": "^4.21.0",
102
102
  "typescript": "^5.9.3"
103
103
  },
104
- "gitHead": "71058a2aa53cc20255502068b0f0615c384d93d2"
104
+ "gitHead": "d7ad4d8a95a9ab73c9eee919e0cdfcc2334845af"
105
105
  }
package/types/schema.d.ts CHANGED
@@ -262,4 +262,6 @@ export interface ResolvedType {
262
262
  enumValues?: string[];
263
263
  /** Possible types for UNION types */
264
264
  possibleTypes?: string[];
265
+ /** Whether this INPUT_OBJECT uses @oneOf (GraphQL 16+ discriminated union input) */
266
+ isOneOf?: boolean;
265
267
  }