@constructive-io/graphql-codegen 4.0.1 → 4.1.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.
- package/cli/handler.d.ts +13 -0
- package/cli/handler.js +74 -0
- package/cli/index.js +11 -60
- package/cli/shared.d.ts +1 -1
- package/cli/shared.js +2 -5
- package/core/codegen/barrel.d.ts +1 -0
- package/core/codegen/barrel.js +5 -2
- package/core/codegen/cli/arg-mapper.d.ts +4 -0
- package/core/codegen/cli/arg-mapper.js +117 -0
- package/core/codegen/cli/command-map-generator.d.ts +16 -0
- package/core/codegen/cli/command-map-generator.js +338 -0
- package/core/codegen/cli/custom-command-generator.d.ts +8 -0
- package/core/codegen/cli/custom-command-generator.js +155 -0
- package/core/codegen/cli/docs-generator.d.ts +26 -0
- package/core/codegen/cli/docs-generator.js +1399 -0
- package/core/codegen/cli/executor-generator.d.ts +11 -0
- package/core/codegen/cli/executor-generator.js +217 -0
- package/core/codegen/cli/index.d.ts +53 -0
- package/core/codegen/cli/index.js +153 -0
- package/core/codegen/cli/infra-generator.d.ts +9 -0
- package/core/codegen/cli/infra-generator.js +1195 -0
- package/core/codegen/cli/table-command-generator.d.ts +7 -0
- package/core/codegen/cli/table-command-generator.js +323 -0
- package/core/codegen/docs-utils.d.ts +30 -0
- package/core/codegen/docs-utils.js +122 -0
- package/core/codegen/hooks-docs-generator.d.ts +6 -0
- package/core/codegen/hooks-docs-generator.js +468 -0
- package/core/codegen/orm/docs-generator.d.ts +6 -0
- package/core/codegen/orm/docs-generator.js +416 -0
- package/core/codegen/target-docs-generator.d.ts +20 -0
- package/core/codegen/target-docs-generator.js +110 -0
- package/core/database/index.d.ts +0 -12
- package/core/database/index.js +2 -19
- package/core/generate.d.ts +34 -2
- package/core/generate.js +453 -12
- package/core/index.d.ts +0 -2
- package/core/index.js +0 -2
- package/core/introspect/source/database.js +2 -2
- package/core/introspect/source/pgpm-module.js +2 -2
- package/core/output/index.d.ts +1 -1
- package/core/output/index.js +1 -2
- package/core/output/writer.d.ts +0 -10
- package/core/output/writer.js +0 -31
- package/esm/cli/handler.d.ts +13 -0
- package/esm/cli/handler.js +71 -0
- package/esm/cli/index.js +11 -60
- package/esm/cli/shared.d.ts +1 -1
- package/esm/cli/shared.js +2 -5
- package/esm/core/codegen/barrel.d.ts +1 -0
- package/esm/core/codegen/barrel.js +5 -2
- package/esm/core/codegen/cli/arg-mapper.d.ts +4 -0
- package/esm/core/codegen/cli/arg-mapper.js +80 -0
- package/esm/core/codegen/cli/command-map-generator.d.ts +16 -0
- package/esm/core/codegen/cli/command-map-generator.js +301 -0
- package/esm/core/codegen/cli/custom-command-generator.d.ts +8 -0
- package/esm/core/codegen/cli/custom-command-generator.js +119 -0
- package/esm/core/codegen/cli/docs-generator.d.ts +26 -0
- package/esm/core/codegen/cli/docs-generator.js +1387 -0
- package/esm/core/codegen/cli/executor-generator.d.ts +11 -0
- package/esm/core/codegen/cli/executor-generator.js +180 -0
- package/esm/core/codegen/cli/index.d.ts +53 -0
- package/esm/core/codegen/cli/index.js +128 -0
- package/esm/core/codegen/cli/infra-generator.d.ts +9 -0
- package/esm/core/codegen/cli/infra-generator.js +1156 -0
- package/esm/core/codegen/cli/table-command-generator.d.ts +7 -0
- package/esm/core/codegen/cli/table-command-generator.js +287 -0
- package/esm/core/codegen/docs-utils.d.ts +30 -0
- package/esm/core/codegen/docs-utils.js +112 -0
- package/esm/core/codegen/hooks-docs-generator.d.ts +6 -0
- package/esm/core/codegen/hooks-docs-generator.js +462 -0
- package/esm/core/codegen/orm/docs-generator.d.ts +6 -0
- package/esm/core/codegen/orm/docs-generator.js +410 -0
- package/esm/core/codegen/target-docs-generator.d.ts +20 -0
- package/esm/core/codegen/target-docs-generator.js +105 -0
- package/esm/core/database/index.d.ts +0 -12
- package/esm/core/database/index.js +1 -17
- package/esm/core/generate.d.ts +34 -2
- package/esm/core/generate.js +417 -12
- package/esm/core/index.d.ts +0 -2
- package/esm/core/index.js +0 -2
- package/esm/core/introspect/source/database.js +2 -2
- package/esm/core/introspect/source/pgpm-module.js +2 -2
- package/esm/core/output/index.d.ts +1 -1
- package/esm/core/output/index.js +1 -1
- package/esm/core/output/writer.d.ts +0 -10
- package/esm/core/output/writer.js +0 -30
- package/esm/generators/index.d.ts +0 -3
- package/esm/generators/index.js +0 -3
- package/esm/index.d.ts +4 -3
- package/esm/index.js +4 -2
- package/esm/types/config.d.ts +78 -0
- package/generators/index.d.ts +0 -3
- package/generators/index.js +0 -3
- package/index.d.ts +4 -3
- package/index.js +7 -2
- package/package.json +8 -7
- package/types/config.d.ts +78 -0
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
import { buildSkillFile, formatArgType, getEditableFields, getReadmeHeader, getReadmeFooter, gqlTypeToJsonSchemaType, } from '../docs-utils';
|
|
2
|
+
import { getScalarFields, getTableNames, getPrimaryKeyInfo, lcFirst, fieldTypeToTs, } from '../utils';
|
|
3
|
+
export function generateOrmReadme(tables, customOperations) {
|
|
4
|
+
const lines = [];
|
|
5
|
+
lines.push(...getReadmeHeader('ORM Client'));
|
|
6
|
+
lines.push('## Setup');
|
|
7
|
+
lines.push('');
|
|
8
|
+
lines.push('```typescript');
|
|
9
|
+
lines.push("import { createClient } from './orm';");
|
|
10
|
+
lines.push('');
|
|
11
|
+
lines.push('const db = createClient({');
|
|
12
|
+
lines.push(" endpoint: 'https://api.example.com/graphql',");
|
|
13
|
+
lines.push(" headers: { Authorization: 'Bearer <token>' },");
|
|
14
|
+
lines.push('});');
|
|
15
|
+
lines.push('```');
|
|
16
|
+
lines.push('');
|
|
17
|
+
lines.push('## Models');
|
|
18
|
+
lines.push('');
|
|
19
|
+
lines.push('| Model | Operations |');
|
|
20
|
+
lines.push('|-------|------------|');
|
|
21
|
+
for (const table of tables) {
|
|
22
|
+
const { singularName } = getTableNames(table);
|
|
23
|
+
lines.push(`| \`${singularName}\` | findMany, findOne, create, update, delete |`);
|
|
24
|
+
}
|
|
25
|
+
lines.push('');
|
|
26
|
+
if (tables.length > 0) {
|
|
27
|
+
lines.push('## Table Operations');
|
|
28
|
+
lines.push('');
|
|
29
|
+
for (const table of tables) {
|
|
30
|
+
const { singularName } = getTableNames(table);
|
|
31
|
+
const pk = getPrimaryKeyInfo(table)[0];
|
|
32
|
+
const scalarFields = getScalarFields(table);
|
|
33
|
+
const editableFields = getEditableFields(table);
|
|
34
|
+
lines.push(`### \`db.${singularName}\``);
|
|
35
|
+
lines.push('');
|
|
36
|
+
lines.push(`CRUD operations for ${table.name} records.`);
|
|
37
|
+
lines.push('');
|
|
38
|
+
lines.push('**Fields:**');
|
|
39
|
+
lines.push('');
|
|
40
|
+
lines.push('| Field | Type | Editable |');
|
|
41
|
+
lines.push('|-------|------|----------|');
|
|
42
|
+
for (const f of scalarFields) {
|
|
43
|
+
const editable = editableFields.some((ef) => ef.name === f.name);
|
|
44
|
+
lines.push(`| \`${f.name}\` | ${f.type.gqlType} | ${editable ? 'Yes' : 'No'} |`);
|
|
45
|
+
}
|
|
46
|
+
lines.push('');
|
|
47
|
+
lines.push('**Operations:**');
|
|
48
|
+
lines.push('');
|
|
49
|
+
lines.push('```typescript');
|
|
50
|
+
lines.push(`// List all ${singularName} records`);
|
|
51
|
+
lines.push(`const items = await db.${singularName}.findMany({ select: { ${scalarFields.map((f) => `${f.name}: true`).join(', ')} } }).execute();`);
|
|
52
|
+
lines.push('');
|
|
53
|
+
lines.push(`// Get one by ${pk.name}`);
|
|
54
|
+
lines.push(`const item = await db.${singularName}.findOne({ where: { ${pk.name}: '<value>' }, select: { ${scalarFields.map((f) => `${f.name}: true`).join(', ')} } }).execute();`);
|
|
55
|
+
lines.push('');
|
|
56
|
+
lines.push(`// Create`);
|
|
57
|
+
lines.push(`const created = await db.${singularName}.create({ data: { ${editableFields.map((f) => `${f.name}: '<value>'`).join(', ')} }, select: { ${pk.name}: true } }).execute();`);
|
|
58
|
+
lines.push('');
|
|
59
|
+
lines.push(`// Update`);
|
|
60
|
+
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('');
|
|
62
|
+
lines.push(`// Delete`);
|
|
63
|
+
lines.push(`const deleted = await db.${singularName}.delete({ where: { ${pk.name}: '<value>' } }).execute();`);
|
|
64
|
+
lines.push('```');
|
|
65
|
+
lines.push('');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (customOperations.length > 0) {
|
|
69
|
+
lines.push('## Custom Operations');
|
|
70
|
+
lines.push('');
|
|
71
|
+
for (const op of customOperations) {
|
|
72
|
+
const accessor = op.kind === 'query' ? 'query' : 'mutation';
|
|
73
|
+
lines.push(`### \`db.${accessor}.${op.name}\``);
|
|
74
|
+
lines.push('');
|
|
75
|
+
lines.push(op.description || op.name);
|
|
76
|
+
lines.push('');
|
|
77
|
+
lines.push(`- **Type:** ${op.kind}`);
|
|
78
|
+
if (op.args.length > 0) {
|
|
79
|
+
lines.push('- **Arguments:**');
|
|
80
|
+
lines.push('');
|
|
81
|
+
lines.push(' | Argument | Type |');
|
|
82
|
+
lines.push(' |----------|------|');
|
|
83
|
+
for (const arg of op.args) {
|
|
84
|
+
lines.push(` | \`${arg.name}\` | ${formatArgType(arg)} |`);
|
|
85
|
+
}
|
|
86
|
+
lines.push('');
|
|
87
|
+
lines.push('```typescript');
|
|
88
|
+
lines.push(`const result = await db.${accessor}.${op.name}({ ${op.args.map((a) => `${a.name}: '<value>'`).join(', ')} }).execute();`);
|
|
89
|
+
lines.push('```');
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
lines.push('- **Arguments:** none');
|
|
93
|
+
lines.push('');
|
|
94
|
+
lines.push('```typescript');
|
|
95
|
+
lines.push(`const result = await db.${accessor}.${op.name}().execute();`);
|
|
96
|
+
lines.push('```');
|
|
97
|
+
}
|
|
98
|
+
lines.push('');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
lines.push(...getReadmeFooter());
|
|
102
|
+
return {
|
|
103
|
+
fileName: 'README.md',
|
|
104
|
+
content: lines.join('\n'),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
export function generateOrmAgentsDocs(tables, customOperations) {
|
|
108
|
+
const lines = [];
|
|
109
|
+
lines.push('# ORM Client - Agent Reference');
|
|
110
|
+
lines.push('');
|
|
111
|
+
lines.push('> @generated by @constructive-io/graphql-codegen - DO NOT EDIT');
|
|
112
|
+
lines.push('> This document is structured for LLM/agent consumption.');
|
|
113
|
+
lines.push('');
|
|
114
|
+
lines.push('## OVERVIEW');
|
|
115
|
+
lines.push('');
|
|
116
|
+
lines.push('Prisma-like ORM client for interacting with a GraphQL API.');
|
|
117
|
+
lines.push('All methods return a query builder. Call `.execute()` to run the query.');
|
|
118
|
+
lines.push('');
|
|
119
|
+
lines.push('## SETUP');
|
|
120
|
+
lines.push('');
|
|
121
|
+
lines.push('```typescript');
|
|
122
|
+
lines.push("import { createClient } from './orm';");
|
|
123
|
+
lines.push('');
|
|
124
|
+
lines.push('const db = createClient({');
|
|
125
|
+
lines.push(" endpoint: 'https://api.example.com/graphql',");
|
|
126
|
+
lines.push(" headers: { Authorization: 'Bearer <token>' },");
|
|
127
|
+
lines.push('});');
|
|
128
|
+
lines.push('```');
|
|
129
|
+
lines.push('');
|
|
130
|
+
lines.push('## MODELS');
|
|
131
|
+
lines.push('');
|
|
132
|
+
for (const table of tables) {
|
|
133
|
+
const { singularName } = getTableNames(table);
|
|
134
|
+
const pk = getPrimaryKeyInfo(table)[0];
|
|
135
|
+
const scalarFields = getScalarFields(table);
|
|
136
|
+
const editableFields = getEditableFields(table);
|
|
137
|
+
lines.push(`### MODEL: ${singularName}`);
|
|
138
|
+
lines.push('');
|
|
139
|
+
lines.push(`Access: \`db.${singularName}\``);
|
|
140
|
+
lines.push('');
|
|
141
|
+
lines.push('```');
|
|
142
|
+
lines.push('METHODS:');
|
|
143
|
+
lines.push(` db.${singularName}.findMany({ select, where?, orderBy?, first?, offset? })`);
|
|
144
|
+
lines.push(` db.${singularName}.findOne({ where: { ${pk.name} }, select })`);
|
|
145
|
+
lines.push(` db.${singularName}.create({ data: { ${editableFields.map((f) => f.name).join(', ')} }, select })`);
|
|
146
|
+
lines.push(` db.${singularName}.update({ where: { ${pk.name} }, data, select })`);
|
|
147
|
+
lines.push(` db.${singularName}.delete({ where: { ${pk.name} } })`);
|
|
148
|
+
lines.push('');
|
|
149
|
+
lines.push('FIELDS:');
|
|
150
|
+
for (const f of scalarFields) {
|
|
151
|
+
const isPk = f.name === pk.name;
|
|
152
|
+
lines.push(` ${f.name}: ${fieldTypeToTs(f.type)}${isPk ? ' (primary key)' : ''}`);
|
|
153
|
+
}
|
|
154
|
+
lines.push('');
|
|
155
|
+
lines.push('EDITABLE FIELDS:');
|
|
156
|
+
for (const f of editableFields) {
|
|
157
|
+
lines.push(` ${f.name}: ${fieldTypeToTs(f.type)}`);
|
|
158
|
+
}
|
|
159
|
+
lines.push('');
|
|
160
|
+
lines.push('OUTPUT: Promise<JSON>');
|
|
161
|
+
lines.push(` findMany: [{ ${scalarFields.map((f) => f.name).join(', ')} }]`);
|
|
162
|
+
lines.push(` findOne: { ${scalarFields.map((f) => f.name).join(', ')} }`);
|
|
163
|
+
lines.push(` create: { ${scalarFields.map((f) => f.name).join(', ')} }`);
|
|
164
|
+
lines.push(` update: { ${scalarFields.map((f) => f.name).join(', ')} }`);
|
|
165
|
+
lines.push(` delete: { ${pk.name} }`);
|
|
166
|
+
lines.push('```');
|
|
167
|
+
lines.push('');
|
|
168
|
+
}
|
|
169
|
+
if (customOperations.length > 0) {
|
|
170
|
+
lines.push('## CUSTOM OPERATIONS');
|
|
171
|
+
lines.push('');
|
|
172
|
+
for (const op of customOperations) {
|
|
173
|
+
const accessor = op.kind === 'query' ? 'query' : 'mutation';
|
|
174
|
+
lines.push(`### OPERATION: ${op.name}`);
|
|
175
|
+
lines.push('');
|
|
176
|
+
lines.push(op.description || op.name);
|
|
177
|
+
lines.push('');
|
|
178
|
+
lines.push('```');
|
|
179
|
+
lines.push(`TYPE: ${op.kind}`);
|
|
180
|
+
lines.push(`ACCESS: db.${accessor}.${op.name}`);
|
|
181
|
+
if (op.args.length > 0) {
|
|
182
|
+
lines.push(`USAGE: db.${accessor}.${op.name}({ ${op.args.map((a) => `${a.name}: <value>`).join(', ')} }).execute()`);
|
|
183
|
+
lines.push('');
|
|
184
|
+
lines.push('INPUT:');
|
|
185
|
+
for (const arg of op.args) {
|
|
186
|
+
lines.push(` ${arg.name}: ${formatArgType(arg)}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
lines.push(`USAGE: db.${accessor}.${op.name}().execute()`);
|
|
191
|
+
lines.push('');
|
|
192
|
+
lines.push('INPUT: none');
|
|
193
|
+
}
|
|
194
|
+
lines.push('');
|
|
195
|
+
lines.push('OUTPUT: Promise<JSON>');
|
|
196
|
+
lines.push('```');
|
|
197
|
+
lines.push('');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
lines.push('## PATTERNS');
|
|
201
|
+
lines.push('');
|
|
202
|
+
lines.push('```typescript');
|
|
203
|
+
lines.push('// All methods require .execute() to run');
|
|
204
|
+
lines.push('const result = await db.modelName.findMany({ select: { id: true } }).execute();');
|
|
205
|
+
lines.push('');
|
|
206
|
+
lines.push('// Select specific fields');
|
|
207
|
+
lines.push('const partial = await db.modelName.findMany({ select: { id: true, name: true } }).execute();');
|
|
208
|
+
lines.push('');
|
|
209
|
+
lines.push('// Filter with where clause');
|
|
210
|
+
lines.push("const filtered = await db.modelName.findMany({ select: { id: true }, where: { name: 'test' } }).execute();");
|
|
211
|
+
lines.push('```');
|
|
212
|
+
lines.push('');
|
|
213
|
+
return {
|
|
214
|
+
fileName: 'AGENTS.md',
|
|
215
|
+
content: lines.join('\n'),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
export function getOrmMcpTools(tables, customOperations) {
|
|
219
|
+
const tools = [];
|
|
220
|
+
for (const table of tables) {
|
|
221
|
+
const { singularName } = getTableNames(table);
|
|
222
|
+
const pk = getPrimaryKeyInfo(table)[0];
|
|
223
|
+
const scalarFields = getScalarFields(table);
|
|
224
|
+
const editableFields = getEditableFields(table);
|
|
225
|
+
tools.push({
|
|
226
|
+
name: `orm_${lcFirst(singularName)}_findMany`,
|
|
227
|
+
description: `List all ${table.name} records via ORM`,
|
|
228
|
+
inputSchema: {
|
|
229
|
+
type: 'object',
|
|
230
|
+
properties: {
|
|
231
|
+
first: {
|
|
232
|
+
type: 'integer',
|
|
233
|
+
description: 'Limit number of results',
|
|
234
|
+
},
|
|
235
|
+
offset: {
|
|
236
|
+
type: 'integer',
|
|
237
|
+
description: 'Offset for pagination',
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
tools.push({
|
|
243
|
+
name: `orm_${lcFirst(singularName)}_findOne`,
|
|
244
|
+
description: `Get a single ${table.name} record by ${pk.name}`,
|
|
245
|
+
inputSchema: {
|
|
246
|
+
type: 'object',
|
|
247
|
+
properties: {
|
|
248
|
+
[pk.name]: {
|
|
249
|
+
type: gqlTypeToJsonSchemaType(pk.gqlType),
|
|
250
|
+
description: `${table.name} ${pk.name}`,
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
required: [pk.name],
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
const createProps = {};
|
|
257
|
+
for (const f of editableFields) {
|
|
258
|
+
createProps[f.name] = {
|
|
259
|
+
type: gqlTypeToJsonSchemaType(f.type.gqlType),
|
|
260
|
+
description: `${table.name} ${f.name}`,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
tools.push({
|
|
264
|
+
name: `orm_${lcFirst(singularName)}_create`,
|
|
265
|
+
description: `Create a new ${table.name} record`,
|
|
266
|
+
inputSchema: {
|
|
267
|
+
type: 'object',
|
|
268
|
+
properties: createProps,
|
|
269
|
+
required: editableFields.map((f) => f.name),
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
const updateProps = {
|
|
273
|
+
[pk.name]: {
|
|
274
|
+
type: gqlTypeToJsonSchemaType(pk.gqlType),
|
|
275
|
+
description: `${table.name} ${pk.name}`,
|
|
276
|
+
},
|
|
277
|
+
};
|
|
278
|
+
for (const f of editableFields) {
|
|
279
|
+
updateProps[f.name] = {
|
|
280
|
+
type: gqlTypeToJsonSchemaType(f.type.gqlType),
|
|
281
|
+
description: `${table.name} ${f.name}`,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
tools.push({
|
|
285
|
+
name: `orm_${lcFirst(singularName)}_update`,
|
|
286
|
+
description: `Update an existing ${table.name} record`,
|
|
287
|
+
inputSchema: {
|
|
288
|
+
type: 'object',
|
|
289
|
+
properties: updateProps,
|
|
290
|
+
required: [pk.name],
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
tools.push({
|
|
294
|
+
name: `orm_${lcFirst(singularName)}_delete`,
|
|
295
|
+
description: `Delete a ${table.name} record by ${pk.name}`,
|
|
296
|
+
inputSchema: {
|
|
297
|
+
type: 'object',
|
|
298
|
+
properties: {
|
|
299
|
+
[pk.name]: {
|
|
300
|
+
type: gqlTypeToJsonSchemaType(pk.gqlType),
|
|
301
|
+
description: `${table.name} ${pk.name}`,
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
required: [pk.name],
|
|
305
|
+
},
|
|
306
|
+
_meta: {
|
|
307
|
+
fields: scalarFields.map((f) => ({
|
|
308
|
+
name: f.name,
|
|
309
|
+
type: f.type.gqlType,
|
|
310
|
+
editable: editableFields.some((ef) => ef.name === f.name),
|
|
311
|
+
primaryKey: f.name === pk.name,
|
|
312
|
+
})),
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
for (const op of customOperations) {
|
|
317
|
+
const accessor = op.kind === 'query' ? 'query' : 'mutation';
|
|
318
|
+
const props = {};
|
|
319
|
+
const required = [];
|
|
320
|
+
for (const arg of op.args) {
|
|
321
|
+
const isRequired = arg.type.kind === 'NON_NULL';
|
|
322
|
+
const baseType = isRequired && arg.type.ofType ? arg.type.ofType : arg.type;
|
|
323
|
+
props[arg.name] = {
|
|
324
|
+
type: gqlTypeToJsonSchemaType(baseType.name ?? 'String'),
|
|
325
|
+
description: arg.description || arg.name,
|
|
326
|
+
};
|
|
327
|
+
if (isRequired) {
|
|
328
|
+
required.push(arg.name);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
tools.push({
|
|
332
|
+
name: `orm_${accessor}_${op.name}`,
|
|
333
|
+
description: op.description || `Execute ${op.name} ${op.kind}`,
|
|
334
|
+
inputSchema: {
|
|
335
|
+
type: 'object',
|
|
336
|
+
properties: props,
|
|
337
|
+
...(required.length > 0 ? { required } : {}),
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
return tools;
|
|
342
|
+
}
|
|
343
|
+
export function generateOrmSkills(tables, customOperations) {
|
|
344
|
+
const files = [];
|
|
345
|
+
for (const table of tables) {
|
|
346
|
+
const { singularName } = getTableNames(table);
|
|
347
|
+
const pk = getPrimaryKeyInfo(table)[0];
|
|
348
|
+
const editableFields = getEditableFields(table);
|
|
349
|
+
files.push({
|
|
350
|
+
fileName: `skills/${lcFirst(singularName)}.md`,
|
|
351
|
+
content: buildSkillFile({
|
|
352
|
+
name: `orm-${lcFirst(singularName)}`,
|
|
353
|
+
description: `ORM operations for ${table.name} records`,
|
|
354
|
+
language: 'typescript',
|
|
355
|
+
usage: [
|
|
356
|
+
`db.${lcFirst(singularName)}.findMany({ select: { id: true } }).execute()`,
|
|
357
|
+
`db.${lcFirst(singularName)}.findOne({ where: { ${pk.name}: '<value>' }, select: { id: true } }).execute()`,
|
|
358
|
+
`db.${lcFirst(singularName)}.create({ data: { ${editableFields.map((f) => `${f.name}: '<value>'`).join(', ')} }, select: { id: true } }).execute()`,
|
|
359
|
+
`db.${lcFirst(singularName)}.update({ where: { ${pk.name}: '<value>' }, data: { ${editableFields[0]?.name || 'field'}: '<new>' }, select: { id: true } }).execute()`,
|
|
360
|
+
`db.${lcFirst(singularName)}.delete({ where: { ${pk.name}: '<value>' } }).execute()`,
|
|
361
|
+
],
|
|
362
|
+
examples: [
|
|
363
|
+
{
|
|
364
|
+
description: `List all ${singularName} records`,
|
|
365
|
+
code: [
|
|
366
|
+
`const items = await db.${lcFirst(singularName)}.findMany({`,
|
|
367
|
+
` select: { ${pk.name}: true, ${editableFields[0]?.name || 'name'}: true }`,
|
|
368
|
+
'}).execute();',
|
|
369
|
+
],
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
description: `Create a ${singularName}`,
|
|
373
|
+
code: [
|
|
374
|
+
`const item = await db.${lcFirst(singularName)}.create({`,
|
|
375
|
+
` data: { ${editableFields.map((f) => `${f.name}: 'value'`).join(', ')} },`,
|
|
376
|
+
` select: { ${pk.name}: true }`,
|
|
377
|
+
'}).execute();',
|
|
378
|
+
],
|
|
379
|
+
},
|
|
380
|
+
],
|
|
381
|
+
}),
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
for (const op of customOperations) {
|
|
385
|
+
const accessor = op.kind === 'query' ? 'query' : 'mutation';
|
|
386
|
+
const callArgs = op.args.length > 0
|
|
387
|
+
? `{ ${op.args.map((a) => `${a.name}: '<value>'`).join(', ')} }`
|
|
388
|
+
: '';
|
|
389
|
+
files.push({
|
|
390
|
+
fileName: `skills/${op.name}.md`,
|
|
391
|
+
content: buildSkillFile({
|
|
392
|
+
name: `orm-${op.name}`,
|
|
393
|
+
description: op.description || `Execute the ${op.name} ${op.kind}`,
|
|
394
|
+
language: 'typescript',
|
|
395
|
+
usage: [
|
|
396
|
+
`db.${accessor}.${op.name}(${callArgs}).execute()`,
|
|
397
|
+
],
|
|
398
|
+
examples: [
|
|
399
|
+
{
|
|
400
|
+
description: `Run ${op.name}`,
|
|
401
|
+
code: [
|
|
402
|
+
`const result = await db.${accessor}.${op.name}(${callArgs}).execute();`,
|
|
403
|
+
],
|
|
404
|
+
},
|
|
405
|
+
],
|
|
406
|
+
}),
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
return files;
|
|
410
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { GraphQLSDKConfigTarget } from '../../types/config';
|
|
2
|
+
import type { GeneratedDocFile, McpTool } from './docs-utils';
|
|
3
|
+
export interface TargetReadmeOptions {
|
|
4
|
+
hasOrm: boolean;
|
|
5
|
+
hasHooks: boolean;
|
|
6
|
+
hasCli: boolean;
|
|
7
|
+
tableCount: number;
|
|
8
|
+
customQueryCount: number;
|
|
9
|
+
customMutationCount: number;
|
|
10
|
+
config: GraphQLSDKConfigTarget;
|
|
11
|
+
}
|
|
12
|
+
export declare function generateTargetReadme(options: TargetReadmeOptions): GeneratedDocFile;
|
|
13
|
+
export declare function generateCombinedMcpConfig(tools: McpTool[], name: string): GeneratedDocFile;
|
|
14
|
+
export interface RootRootReadmeTarget {
|
|
15
|
+
name: string;
|
|
16
|
+
output: string;
|
|
17
|
+
endpoint?: string;
|
|
18
|
+
generators: string[];
|
|
19
|
+
}
|
|
20
|
+
export declare function generateRootRootReadme(targets: RootRootReadmeTarget[]): GeneratedDocFile;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { getReadmeHeader, getReadmeFooter } from './docs-utils';
|
|
2
|
+
export function generateTargetReadme(options) {
|
|
3
|
+
const { hasOrm, hasHooks, hasCli, tableCount, customQueryCount, customMutationCount, config, } = options;
|
|
4
|
+
const lines = [];
|
|
5
|
+
lines.push(...getReadmeHeader('Generated GraphQL SDK'));
|
|
6
|
+
lines.push('## Overview');
|
|
7
|
+
lines.push('');
|
|
8
|
+
lines.push(`- **Tables:** ${tableCount}`);
|
|
9
|
+
lines.push(`- **Custom queries:** ${customQueryCount}`);
|
|
10
|
+
lines.push(`- **Custom mutations:** ${customMutationCount}`);
|
|
11
|
+
lines.push('');
|
|
12
|
+
const generators = [];
|
|
13
|
+
if (hasOrm)
|
|
14
|
+
generators.push('ORM');
|
|
15
|
+
if (hasHooks)
|
|
16
|
+
generators.push('React Query');
|
|
17
|
+
if (hasCli)
|
|
18
|
+
generators.push('CLI');
|
|
19
|
+
lines.push(`**Generators:** ${generators.join(', ')}`);
|
|
20
|
+
lines.push('');
|
|
21
|
+
if (config.endpoint) {
|
|
22
|
+
lines.push(`**Endpoint:** \`${config.endpoint}\``);
|
|
23
|
+
lines.push('');
|
|
24
|
+
}
|
|
25
|
+
lines.push('## Modules');
|
|
26
|
+
lines.push('');
|
|
27
|
+
if (hasOrm) {
|
|
28
|
+
lines.push('### ORM Client (`./orm`)');
|
|
29
|
+
lines.push('');
|
|
30
|
+
lines.push('Prisma-like ORM client for programmatic GraphQL access.');
|
|
31
|
+
lines.push('');
|
|
32
|
+
lines.push('```typescript');
|
|
33
|
+
lines.push("import { createClient } from './orm';");
|
|
34
|
+
lines.push('');
|
|
35
|
+
lines.push('const db = createClient({');
|
|
36
|
+
lines.push(" endpoint: 'https://api.example.com/graphql',");
|
|
37
|
+
lines.push('});');
|
|
38
|
+
lines.push('```');
|
|
39
|
+
lines.push('');
|
|
40
|
+
lines.push('See [orm/README.md](./orm/README.md) for full API reference.');
|
|
41
|
+
lines.push('');
|
|
42
|
+
}
|
|
43
|
+
if (hasHooks) {
|
|
44
|
+
lines.push('### React Query Hooks (`./hooks`)');
|
|
45
|
+
lines.push('');
|
|
46
|
+
lines.push('Type-safe React Query hooks for data fetching and mutations.');
|
|
47
|
+
lines.push('');
|
|
48
|
+
lines.push('```typescript');
|
|
49
|
+
lines.push("import { configure } from './hooks';");
|
|
50
|
+
lines.push("import { useCarsQuery } from './hooks';");
|
|
51
|
+
lines.push('');
|
|
52
|
+
lines.push("configure({ endpoint: 'https://api.example.com/graphql' });");
|
|
53
|
+
lines.push('```');
|
|
54
|
+
lines.push('');
|
|
55
|
+
lines.push('See [hooks/README.md](./hooks/README.md) for full hook reference.');
|
|
56
|
+
lines.push('');
|
|
57
|
+
}
|
|
58
|
+
if (hasCli) {
|
|
59
|
+
const toolName = typeof config.cli === 'object' && config.cli?.toolName
|
|
60
|
+
? config.cli.toolName
|
|
61
|
+
: 'app';
|
|
62
|
+
lines.push(`### CLI Commands (\`./cli\`)`);
|
|
63
|
+
lines.push('');
|
|
64
|
+
lines.push(`inquirerer-based CLI commands for \`${toolName}\`.`);
|
|
65
|
+
lines.push('');
|
|
66
|
+
lines.push('See [cli/README.md](./cli/README.md) for command reference.');
|
|
67
|
+
lines.push('');
|
|
68
|
+
}
|
|
69
|
+
lines.push(...getReadmeFooter());
|
|
70
|
+
return {
|
|
71
|
+
fileName: 'README.md',
|
|
72
|
+
content: lines.join('\n'),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export function generateCombinedMcpConfig(tools, name) {
|
|
76
|
+
const mcpConfig = {
|
|
77
|
+
name,
|
|
78
|
+
version: '1.0.0',
|
|
79
|
+
description: `MCP tool definitions for ${name} SDK (auto-generated from GraphQL schema)`,
|
|
80
|
+
tools,
|
|
81
|
+
};
|
|
82
|
+
return {
|
|
83
|
+
fileName: 'mcp.json',
|
|
84
|
+
content: JSON.stringify(mcpConfig, null, 2) + '\n',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
export function generateRootRootReadme(targets) {
|
|
88
|
+
const lines = [];
|
|
89
|
+
lines.push(...getReadmeHeader('GraphQL SDK'));
|
|
90
|
+
lines.push('## APIs');
|
|
91
|
+
lines.push('');
|
|
92
|
+
lines.push('| API | Endpoint | Generators | Docs |');
|
|
93
|
+
lines.push('|-----|----------|------------|------|');
|
|
94
|
+
for (const target of targets) {
|
|
95
|
+
const endpoint = target.endpoint || '-';
|
|
96
|
+
const gens = target.generators.join(', ');
|
|
97
|
+
lines.push(`| ${target.name} | ${endpoint} | ${gens} | [${target.output}/README.md](${target.output}/README.md) |`);
|
|
98
|
+
}
|
|
99
|
+
lines.push('');
|
|
100
|
+
lines.push(...getReadmeFooter());
|
|
101
|
+
return {
|
|
102
|
+
fileName: 'README.md',
|
|
103
|
+
content: lines.join('\n'),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
@@ -29,15 +29,3 @@ export interface BuildSchemaFromDatabaseResult {
|
|
|
29
29
|
* @returns The path to the generated schema file and the SDL content
|
|
30
30
|
*/
|
|
31
31
|
export declare function buildSchemaFromDatabase(options: BuildSchemaFromDatabaseOptions): Promise<BuildSchemaFromDatabaseResult>;
|
|
32
|
-
/**
|
|
33
|
-
* Build a GraphQL schema SDL string from a PostgreSQL database without writing to file.
|
|
34
|
-
*
|
|
35
|
-
* This is a convenience wrapper around buildSchemaSDL from graphql-server.
|
|
36
|
-
*
|
|
37
|
-
* @param options - Configuration options
|
|
38
|
-
* @returns The SDL content as a string
|
|
39
|
-
*/
|
|
40
|
-
export declare function buildSchemaSDLFromDatabase(options: {
|
|
41
|
-
database: string;
|
|
42
|
-
schemas: string[];
|
|
43
|
-
}): Promise<string>;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import * as fs from 'node:fs';
|
|
7
7
|
import * as path from 'node:path';
|
|
8
|
-
import { buildSchemaSDL } from '
|
|
8
|
+
import { buildSchemaSDL } from 'graphile-schema';
|
|
9
9
|
/**
|
|
10
10
|
* Build a GraphQL schema from a PostgreSQL database and write it to a file.
|
|
11
11
|
*
|
|
@@ -29,19 +29,3 @@ export async function buildSchemaFromDatabase(options) {
|
|
|
29
29
|
await fs.promises.writeFile(schemaPath, sdl, 'utf-8');
|
|
30
30
|
return { schemaPath, sdl };
|
|
31
31
|
}
|
|
32
|
-
/**
|
|
33
|
-
* Build a GraphQL schema SDL string from a PostgreSQL database without writing to file.
|
|
34
|
-
*
|
|
35
|
-
* This is a convenience wrapper around buildSchemaSDL from graphql-server.
|
|
36
|
-
*
|
|
37
|
-
* @param options - Configuration options
|
|
38
|
-
* @returns The SDL content as a string
|
|
39
|
-
*/
|
|
40
|
-
export async function buildSchemaSDLFromDatabase(options) {
|
|
41
|
-
const { database, schemas } = options;
|
|
42
|
-
// PostGraphile v5 resolves role/settings via preset configuration.
|
|
43
|
-
return buildSchemaSDL({
|
|
44
|
-
database,
|
|
45
|
-
schemas,
|
|
46
|
-
});
|
|
47
|
-
}
|
package/esm/core/generate.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import type { GraphQLSDKConfigTarget } from '../types/config';
|
|
1
|
+
import type { CliConfig, GraphQLSDKConfigTarget } from '../types/config';
|
|
2
|
+
import type { CleanOperation, CleanTable } from '../types/schema';
|
|
2
3
|
export interface GenerateOptions extends GraphQLSDKConfigTarget {
|
|
3
4
|
authorization?: string;
|
|
4
5
|
verbose?: boolean;
|
|
5
6
|
dryRun?: boolean;
|
|
6
7
|
skipCustomOperations?: boolean;
|
|
8
|
+
schemaOnly?: boolean;
|
|
9
|
+
schemaOnlyOutput?: string;
|
|
10
|
+
schemaOnlyFilename?: string;
|
|
7
11
|
}
|
|
8
12
|
export interface GenerateResult {
|
|
9
13
|
success: boolean;
|
|
@@ -12,6 +16,13 @@ export interface GenerateResult {
|
|
|
12
16
|
tables?: string[];
|
|
13
17
|
filesWritten?: string[];
|
|
14
18
|
errors?: string[];
|
|
19
|
+
pipelineData?: {
|
|
20
|
+
tables: CleanTable[];
|
|
21
|
+
customOperations: {
|
|
22
|
+
queries: CleanOperation[];
|
|
23
|
+
mutations: CleanOperation[];
|
|
24
|
+
};
|
|
25
|
+
};
|
|
15
26
|
}
|
|
16
27
|
/**
|
|
17
28
|
* Main generate function - takes a single config and generates code
|
|
@@ -19,4 +30,25 @@ export interface GenerateResult {
|
|
|
19
30
|
* This is the primary entry point for programmatic usage.
|
|
20
31
|
* For multiple configs, call this function in a loop.
|
|
21
32
|
*/
|
|
22
|
-
export
|
|
33
|
+
export interface GenerateInternalOptions {
|
|
34
|
+
skipCli?: boolean;
|
|
35
|
+
}
|
|
36
|
+
export declare function generate(options?: GenerateOptions, internalOptions?: GenerateInternalOptions): Promise<GenerateResult>;
|
|
37
|
+
export declare function expandApiNamesToMultiTarget(config: GraphQLSDKConfigTarget): Record<string, GraphQLSDKConfigTarget> | null;
|
|
38
|
+
export declare function expandSchemaDirToMultiTarget(config: GraphQLSDKConfigTarget): Record<string, GraphQLSDKConfigTarget> | null;
|
|
39
|
+
export interface GenerateMultiOptions {
|
|
40
|
+
configs: Record<string, GraphQLSDKConfigTarget>;
|
|
41
|
+
cliOverrides?: Partial<GraphQLSDKConfigTarget>;
|
|
42
|
+
verbose?: boolean;
|
|
43
|
+
dryRun?: boolean;
|
|
44
|
+
schemaOnly?: boolean;
|
|
45
|
+
unifiedCli?: CliConfig | boolean;
|
|
46
|
+
}
|
|
47
|
+
export interface GenerateMultiResult {
|
|
48
|
+
results: Array<{
|
|
49
|
+
name: string;
|
|
50
|
+
result: GenerateResult;
|
|
51
|
+
}>;
|
|
52
|
+
hasError: boolean;
|
|
53
|
+
}
|
|
54
|
+
export declare function generateMulti(options: GenerateMultiOptions): Promise<GenerateMultiResult>;
|