@claudetools/tools 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +9 -1
- package/dist/codedna/__tests__/examples/mongoose-example.d.ts +6 -0
- package/dist/codedna/__tests__/examples/mongoose-example.js +163 -0
- package/dist/codedna/__tests__/fixtures/typeorm-production-test.d.ts +1 -0
- package/dist/codedna/__tests__/fixtures/typeorm-production-test.js +231 -0
- package/dist/codedna/__tests__/fixtures/typeorm-test.d.ts +1 -0
- package/dist/codedna/__tests__/fixtures/typeorm-test.js +124 -0
- package/dist/codedna/__tests__/laravel-output-review.d.ts +1 -0
- package/dist/codedna/__tests__/laravel-output-review.js +249 -0
- package/dist/codedna/__tests__/mongoose-output-test.d.ts +1 -0
- package/dist/codedna/__tests__/mongoose-output-test.js +178 -0
- package/dist/codedna/examples/radix-example.d.ts +2 -0
- package/dist/codedna/examples/radix-example.js +259 -0
- package/dist/codedna/index.d.ts +5 -3
- package/dist/codedna/index.js +6 -3
- package/dist/codedna/kappa-ast.d.ts +143 -5
- package/dist/codedna/kappa-drizzle-generator.js +8 -5
- package/dist/codedna/kappa-gofiber-generator.d.ts +65 -0
- package/dist/codedna/kappa-gofiber-generator.js +587 -0
- package/dist/codedna/kappa-laravel-generator.d.ts +68 -0
- package/dist/codedna/kappa-laravel-generator.js +741 -0
- package/dist/codedna/kappa-lexer.d.ts +44 -0
- package/dist/codedna/kappa-lexer.js +124 -0
- package/dist/codedna/kappa-mantine-generator.d.ts +65 -0
- package/dist/codedna/kappa-mantine-generator.js +518 -0
- package/dist/codedna/kappa-mongoose-generator.d.ts +44 -0
- package/dist/codedna/kappa-mongoose-generator.js +442 -0
- package/dist/codedna/kappa-parser.d.ts +43 -1
- package/dist/codedna/kappa-parser.js +601 -0
- package/dist/codedna/kappa-radix-generator.d.ts +61 -0
- package/dist/codedna/kappa-radix-generator.js +566 -0
- package/dist/codedna/kappa-typeorm-generator.d.ts +59 -0
- package/dist/codedna/kappa-typeorm-generator.js +723 -0
- package/dist/codedna/kappa-vitest-generator.d.ts +85 -0
- package/dist/codedna/kappa-vitest-generator.js +739 -0
- package/dist/codedna/parser.js +26 -1
- package/dist/codegen/cloud-client.d.ts +160 -0
- package/dist/codegen/cloud-client.js +195 -0
- package/dist/codegen/codegen-tool.d.ts +35 -0
- package/dist/codegen/codegen-tool.js +312 -0
- package/dist/codegen/field-inference.d.ts +24 -0
- package/dist/codegen/field-inference.js +101 -0
- package/dist/codegen/form-parser.d.ts +13 -0
- package/dist/codegen/form-parser.js +186 -0
- package/dist/codegen/index.d.ts +2 -0
- package/dist/codegen/index.js +4 -0
- package/dist/codegen/natural-parser.d.ts +50 -0
- package/dist/codegen/natural-parser.js +769 -0
- package/dist/handlers/codedna-handlers.d.ts +1 -1
- package/dist/handlers/codegen-handlers.d.ts +20 -0
- package/dist/handlers/codegen-handlers.js +60 -0
- package/dist/handlers/kappa-handlers.d.ts +97 -0
- package/dist/handlers/kappa-handlers.js +408 -0
- package/dist/handlers/tool-handlers.js +124 -221
- package/dist/helpers/api-client.js +48 -3
- package/dist/helpers/compact-formatter.d.ts +9 -2
- package/dist/helpers/compact-formatter.js +26 -2
- package/dist/helpers/config.d.ts +7 -2
- package/dist/helpers/config.js +25 -10
- package/dist/helpers/session-validation.d.ts +1 -1
- package/dist/helpers/session-validation.js +2 -4
- package/dist/helpers/tasks.d.ts +21 -0
- package/dist/helpers/tasks.js +52 -0
- package/dist/helpers/workers.d.ts +1 -1
- package/dist/helpers/workers.js +19 -19
- package/dist/setup.d.ts +1 -0
- package/dist/setup.js +228 -3
- package/dist/templates/claude-md.d.ts +1 -1
- package/dist/templates/claude-md.js +37 -152
- package/dist/templates/orchestrator-prompt.d.ts +2 -2
- package/dist/templates/orchestrator-prompt.js +31 -38
- package/dist/templates/self-critique.d.ts +50 -0
- package/dist/templates/self-critique.js +209 -0
- package/dist/templates/worker-prompt.d.ts +3 -3
- package/dist/templates/worker-prompt.js +18 -18
- package/dist/tools.js +77 -413
- package/docs/codedna/generator-testing-summary.md +205 -0
- package/docs/codedna/radix-ui-generator.md +478 -0
- package/docs/kappa-gofiber-generator.md +274 -0
- package/docs/kappa-laravel-fixes.md +172 -0
- package/docs/kappa-mongoose-generator.md +322 -0
- package/docs/kappa-vitest-generator.md +337 -0
- package/package.json +1 -1
- package/dist/context/deduplication.test.d.ts +0 -6
- package/dist/context/deduplication.test.js +0 -84
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Kappa v2.5 Go/Fiber Generator
|
|
3
|
+
// =============================================================================
|
|
4
|
+
//
|
|
5
|
+
// Generates Go code with Fiber framework from Kappa specifications.
|
|
6
|
+
// Supports: API routes, GORM models, validation.
|
|
7
|
+
//
|
|
8
|
+
// =============================================================================
|
|
9
|
+
// Type Mappings
|
|
10
|
+
// =============================================================================
|
|
11
|
+
const PRIMITIVE_GO_MAP = {
|
|
12
|
+
string: 'string',
|
|
13
|
+
int: 'int',
|
|
14
|
+
float: 'float64',
|
|
15
|
+
bool: 'bool',
|
|
16
|
+
timestamp: 'time.Time',
|
|
17
|
+
date: 'time.Time',
|
|
18
|
+
time: 'time.Time',
|
|
19
|
+
duration: 'time.Duration',
|
|
20
|
+
uuid: 'uuid.UUID',
|
|
21
|
+
email: 'string',
|
|
22
|
+
url: 'string',
|
|
23
|
+
phone: 'string',
|
|
24
|
+
slug: 'string',
|
|
25
|
+
markdown: 'string',
|
|
26
|
+
json: 'json.RawMessage',
|
|
27
|
+
};
|
|
28
|
+
const CRUD_METHOD_MAP = {
|
|
29
|
+
create: 'Post',
|
|
30
|
+
read: 'Get',
|
|
31
|
+
update: 'Patch',
|
|
32
|
+
delete: 'Delete',
|
|
33
|
+
list: 'Get',
|
|
34
|
+
};
|
|
35
|
+
const CRUD_PATH_SUFFIX = {
|
|
36
|
+
create: '',
|
|
37
|
+
read: '/:id',
|
|
38
|
+
update: '/:id',
|
|
39
|
+
delete: '/:id',
|
|
40
|
+
list: '',
|
|
41
|
+
};
|
|
42
|
+
const GORM_TAGS = {
|
|
43
|
+
string: '',
|
|
44
|
+
int: '',
|
|
45
|
+
float: '',
|
|
46
|
+
bool: '',
|
|
47
|
+
timestamp: '',
|
|
48
|
+
date: 'type:date',
|
|
49
|
+
time: 'type:time',
|
|
50
|
+
duration: '',
|
|
51
|
+
uuid: 'type:uuid',
|
|
52
|
+
email: '',
|
|
53
|
+
url: '',
|
|
54
|
+
phone: '',
|
|
55
|
+
slug: 'index',
|
|
56
|
+
markdown: 'type:text',
|
|
57
|
+
json: 'type:jsonb',
|
|
58
|
+
};
|
|
59
|
+
// =============================================================================
|
|
60
|
+
// Generator Class
|
|
61
|
+
// =============================================================================
|
|
62
|
+
export class KappaGoFiberGenerator {
|
|
63
|
+
packageName;
|
|
64
|
+
provenance;
|
|
65
|
+
gorm;
|
|
66
|
+
validation;
|
|
67
|
+
basePath;
|
|
68
|
+
modulePath;
|
|
69
|
+
constructor(options = {}) {
|
|
70
|
+
this.packageName = options.packageName ?? 'main';
|
|
71
|
+
this.provenance = options.provenance ?? true;
|
|
72
|
+
this.gorm = options.gorm ?? true;
|
|
73
|
+
this.validation = options.validation ?? true;
|
|
74
|
+
this.basePath = options.basePath ?? '/api';
|
|
75
|
+
this.modulePath = options.modulePath ?? 'app';
|
|
76
|
+
}
|
|
77
|
+
// ===========================================================================
|
|
78
|
+
// Main Generation Methods
|
|
79
|
+
// ===========================================================================
|
|
80
|
+
generateFromAPIs(apis) {
|
|
81
|
+
return {
|
|
82
|
+
routes: this.generateRoutes(apis),
|
|
83
|
+
handlers: this.generateHandlers(apis),
|
|
84
|
+
types: this.generateRequestTypes(apis),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
generateFromEntities(entities) {
|
|
88
|
+
return {
|
|
89
|
+
routes: '',
|
|
90
|
+
models: this.generateModels(entities),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
generateAll(apis, entities) {
|
|
94
|
+
return {
|
|
95
|
+
routes: this.generateRoutes(apis),
|
|
96
|
+
handlers: this.generateHandlers(apis),
|
|
97
|
+
types: this.generateRequestTypes(apis),
|
|
98
|
+
models: this.generateModels(entities),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
// ===========================================================================
|
|
102
|
+
// Route Generation
|
|
103
|
+
// ===========================================================================
|
|
104
|
+
generateRoutes(apis) {
|
|
105
|
+
const lines = [];
|
|
106
|
+
const routes = this.extractRoutes(apis);
|
|
107
|
+
// Header
|
|
108
|
+
if (this.provenance) {
|
|
109
|
+
lines.push('// Generated by Kappa v2.5 CodeDNA');
|
|
110
|
+
lines.push('// Framework: Go/Fiber');
|
|
111
|
+
lines.push('');
|
|
112
|
+
}
|
|
113
|
+
lines.push(`package ${this.packageName}`);
|
|
114
|
+
lines.push('');
|
|
115
|
+
// Imports
|
|
116
|
+
lines.push('import (');
|
|
117
|
+
lines.push('\t"github.com/gofiber/fiber/v2"');
|
|
118
|
+
if (routes.some((r) => r.requiresAuth)) {
|
|
119
|
+
lines.push(`\t"${this.modulePath}/middleware"`);
|
|
120
|
+
}
|
|
121
|
+
lines.push(')');
|
|
122
|
+
lines.push('');
|
|
123
|
+
// Setup function
|
|
124
|
+
lines.push('// SetupRoutes configures all API routes');
|
|
125
|
+
lines.push('func SetupRoutes(app *fiber.App) {');
|
|
126
|
+
lines.push(`\tapi := app.Group("${this.basePath}")`);
|
|
127
|
+
lines.push('');
|
|
128
|
+
// Group routes by API
|
|
129
|
+
for (const api of apis) {
|
|
130
|
+
const apiRoutes = routes.filter((r) => this.isRouteFromAPI(r, api));
|
|
131
|
+
if (apiRoutes.length > 0) {
|
|
132
|
+
lines.push(`\t// ${api.name}`);
|
|
133
|
+
for (const route of apiRoutes) {
|
|
134
|
+
const middleware = route.requiresAuth ? 'middleware.RequireAuth, ' : '';
|
|
135
|
+
lines.push(`\tapi.${route.method}("${route.path}", ${middleware}${route.handlerName})`);
|
|
136
|
+
}
|
|
137
|
+
lines.push('');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
lines.push('}');
|
|
141
|
+
lines.push('');
|
|
142
|
+
return lines.join('\n');
|
|
143
|
+
}
|
|
144
|
+
isRouteFromAPI(route, api) {
|
|
145
|
+
// Check explicit operations
|
|
146
|
+
if (api.operations?.some((op) => op.name === route.operationName)) {
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
// Check CRUD operations
|
|
150
|
+
for (const crud of api.crud || []) {
|
|
151
|
+
if (route.operationName.toLowerCase().endsWith(crud.entity.toLowerCase())) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
extractRoutes(apis) {
|
|
158
|
+
const routes = [];
|
|
159
|
+
for (const api of apis) {
|
|
160
|
+
const apiPath = `/${this.toKebabCase(api.name)}`;
|
|
161
|
+
// Convert CRUD shorthand (crud is an array)
|
|
162
|
+
for (const crud of api.crud || []) {
|
|
163
|
+
routes.push(...this.crudToRoutes(crud, apiPath));
|
|
164
|
+
}
|
|
165
|
+
// Convert explicit operations
|
|
166
|
+
if (api.operations) {
|
|
167
|
+
for (const op of api.operations) {
|
|
168
|
+
routes.push(this.operationToRoute(op, apiPath));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return routes;
|
|
173
|
+
}
|
|
174
|
+
operationToRoute(op, apiPath) {
|
|
175
|
+
const method = this.inferMethod(op.name);
|
|
176
|
+
const path = `${apiPath}/${this.toKebabCase(op.name)}`;
|
|
177
|
+
return {
|
|
178
|
+
method,
|
|
179
|
+
path,
|
|
180
|
+
handlerName: `Handle${this.toPascalCase(op.name)}`,
|
|
181
|
+
operationName: op.name,
|
|
182
|
+
effects: op.effects,
|
|
183
|
+
requiresAuth: op.effects.includes('Auth'),
|
|
184
|
+
parameters: op.parameters,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
crudToRoutes(crud, apiPath) {
|
|
188
|
+
const routes = [];
|
|
189
|
+
const entityPath = `${apiPath}/${this.toKebabCase(crud.entity)}s`;
|
|
190
|
+
for (const action of crud.actions) {
|
|
191
|
+
const method = CRUD_METHOD_MAP[action.action] ?? 'Get';
|
|
192
|
+
const pathSuffix = CRUD_PATH_SUFFIX[action.action] ?? '';
|
|
193
|
+
routes.push({
|
|
194
|
+
method,
|
|
195
|
+
path: `${entityPath}${pathSuffix}`,
|
|
196
|
+
handlerName: `Handle${this.toPascalCase(action.action)}${crud.entity}`,
|
|
197
|
+
operationName: `${action.action}${crud.entity}`,
|
|
198
|
+
effects: action.effects,
|
|
199
|
+
requiresAuth: action.effects.includes('Auth'),
|
|
200
|
+
parameters: [],
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
return routes;
|
|
204
|
+
}
|
|
205
|
+
inferMethod(name) {
|
|
206
|
+
const lower = name.toLowerCase();
|
|
207
|
+
if (lower.startsWith('get') ||
|
|
208
|
+
lower.startsWith('list') ||
|
|
209
|
+
lower.startsWith('find') ||
|
|
210
|
+
lower.startsWith('fetch')) {
|
|
211
|
+
return 'Get';
|
|
212
|
+
}
|
|
213
|
+
if (lower.startsWith('create') || lower.startsWith('add') || lower.startsWith('register')) {
|
|
214
|
+
return 'Post';
|
|
215
|
+
}
|
|
216
|
+
if (lower.startsWith('update') || lower.startsWith('edit')) {
|
|
217
|
+
return 'Patch';
|
|
218
|
+
}
|
|
219
|
+
if (lower.startsWith('delete') || lower.startsWith('remove')) {
|
|
220
|
+
return 'Delete';
|
|
221
|
+
}
|
|
222
|
+
return 'Post';
|
|
223
|
+
}
|
|
224
|
+
// ===========================================================================
|
|
225
|
+
// Handler Generation
|
|
226
|
+
// ===========================================================================
|
|
227
|
+
generateHandlers(apis) {
|
|
228
|
+
const lines = [];
|
|
229
|
+
const routes = this.extractRoutes(apis);
|
|
230
|
+
if (this.provenance) {
|
|
231
|
+
lines.push('// Generated by Kappa v2.5 CodeDNA');
|
|
232
|
+
lines.push('// Framework: Go/Fiber');
|
|
233
|
+
lines.push('');
|
|
234
|
+
}
|
|
235
|
+
lines.push(`package ${this.packageName}`);
|
|
236
|
+
lines.push('');
|
|
237
|
+
lines.push('import (');
|
|
238
|
+
lines.push('\t"github.com/gofiber/fiber/v2"');
|
|
239
|
+
lines.push('\t"gorm.io/gorm"');
|
|
240
|
+
lines.push(')');
|
|
241
|
+
lines.push('');
|
|
242
|
+
lines.push('// db is the database instance (inject or initialize)');
|
|
243
|
+
lines.push('var db *gorm.DB');
|
|
244
|
+
lines.push('');
|
|
245
|
+
for (const route of routes) {
|
|
246
|
+
lines.push(this.generateHandler(route));
|
|
247
|
+
lines.push('');
|
|
248
|
+
}
|
|
249
|
+
return lines.join('\n');
|
|
250
|
+
}
|
|
251
|
+
generateHandler(route) {
|
|
252
|
+
const lines = [];
|
|
253
|
+
lines.push(`// ${route.handlerName} handles ${route.method} ${route.path}`);
|
|
254
|
+
if (route.effects.length > 0) {
|
|
255
|
+
lines.push(`// Effects: ${route.effects.join(', ')}`);
|
|
256
|
+
}
|
|
257
|
+
lines.push(`func ${route.handlerName}(c *fiber.Ctx) error {`);
|
|
258
|
+
// Check if this is a CRUD operation and generate proper implementation
|
|
259
|
+
const opLower = route.operationName.toLowerCase();
|
|
260
|
+
if (opLower.startsWith('list')) {
|
|
261
|
+
lines.push(...this.generateListHandler(route));
|
|
262
|
+
}
|
|
263
|
+
else if (opLower.startsWith('create')) {
|
|
264
|
+
lines.push(...this.generateCreateHandler(route));
|
|
265
|
+
}
|
|
266
|
+
else if (opLower.startsWith('read') || opLower.startsWith('get')) {
|
|
267
|
+
lines.push(...this.generateReadHandler(route));
|
|
268
|
+
}
|
|
269
|
+
else if (opLower.startsWith('update')) {
|
|
270
|
+
lines.push(...this.generateUpdateHandler(route));
|
|
271
|
+
}
|
|
272
|
+
else if (opLower.startsWith('delete')) {
|
|
273
|
+
lines.push(...this.generateDeleteHandler(route));
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
// Custom operation - generate TODO stub
|
|
277
|
+
lines.push(`\t// TODO: Implement ${route.operationName}`);
|
|
278
|
+
lines.push('\treturn c.Status(501).JSON(fiber.Map{');
|
|
279
|
+
lines.push('\t\t"error": "Not implemented",');
|
|
280
|
+
lines.push('\t})');
|
|
281
|
+
}
|
|
282
|
+
lines.push('}');
|
|
283
|
+
return lines.join('\n');
|
|
284
|
+
}
|
|
285
|
+
generateListHandler(route) {
|
|
286
|
+
const entityName = this.extractEntityName(route.operationName);
|
|
287
|
+
return [
|
|
288
|
+
'\tvar items []' + entityName,
|
|
289
|
+
'\t',
|
|
290
|
+
'\t// TODO: Add pagination, filtering, sorting',
|
|
291
|
+
'\tif err := db.Find(&items).Error; err != nil {',
|
|
292
|
+
'\t\treturn c.Status(500).JSON(fiber.Map{"error": err.Error()})',
|
|
293
|
+
'\t}',
|
|
294
|
+
'\t',
|
|
295
|
+
'\treturn c.JSON(items)',
|
|
296
|
+
];
|
|
297
|
+
}
|
|
298
|
+
generateCreateHandler(route) {
|
|
299
|
+
const entityName = this.extractEntityName(route.operationName);
|
|
300
|
+
return [
|
|
301
|
+
'\tvar item ' + entityName,
|
|
302
|
+
'\t',
|
|
303
|
+
'\tif err := c.BodyParser(&item); err != nil {',
|
|
304
|
+
'\t\treturn c.Status(400).JSON(fiber.Map{"error": "Invalid request body"})',
|
|
305
|
+
'\t}',
|
|
306
|
+
'\t',
|
|
307
|
+
'\t// TODO: Add validation',
|
|
308
|
+
'\tif err := db.Create(&item).Error; err != nil {',
|
|
309
|
+
'\t\treturn c.Status(500).JSON(fiber.Map{"error": err.Error()})',
|
|
310
|
+
'\t}',
|
|
311
|
+
'\t',
|
|
312
|
+
'\treturn c.Status(201).JSON(item)',
|
|
313
|
+
];
|
|
314
|
+
}
|
|
315
|
+
generateReadHandler(route) {
|
|
316
|
+
const entityName = this.extractEntityName(route.operationName);
|
|
317
|
+
return [
|
|
318
|
+
'\tid := c.Params("id")',
|
|
319
|
+
'\tvar item ' + entityName,
|
|
320
|
+
'\t',
|
|
321
|
+
'\tif err := db.First(&item, "id = ?", id).Error; err != nil {',
|
|
322
|
+
'\t\treturn c.Status(404).JSON(fiber.Map{"error": "Not found"})',
|
|
323
|
+
'\t}',
|
|
324
|
+
'\t',
|
|
325
|
+
'\treturn c.JSON(item)',
|
|
326
|
+
];
|
|
327
|
+
}
|
|
328
|
+
generateUpdateHandler(route) {
|
|
329
|
+
const entityName = this.extractEntityName(route.operationName);
|
|
330
|
+
return [
|
|
331
|
+
'\tid := c.Params("id")',
|
|
332
|
+
'\tvar item ' + entityName,
|
|
333
|
+
'\t',
|
|
334
|
+
'\tif err := db.First(&item, "id = ?", id).Error; err != nil {',
|
|
335
|
+
'\t\treturn c.Status(404).JSON(fiber.Map{"error": "Not found"})',
|
|
336
|
+
'\t}',
|
|
337
|
+
'\t',
|
|
338
|
+
'\tif err := c.BodyParser(&item); err != nil {',
|
|
339
|
+
'\t\treturn c.Status(400).JSON(fiber.Map{"error": "Invalid request body"})',
|
|
340
|
+
'\t}',
|
|
341
|
+
'\t',
|
|
342
|
+
'\t// TODO: Add validation',
|
|
343
|
+
'\tif err := db.Save(&item).Error; err != nil {',
|
|
344
|
+
'\t\treturn c.Status(500).JSON(fiber.Map{"error": err.Error()})',
|
|
345
|
+
'\t}',
|
|
346
|
+
'\t',
|
|
347
|
+
'\treturn c.JSON(item)',
|
|
348
|
+
];
|
|
349
|
+
}
|
|
350
|
+
generateDeleteHandler(route) {
|
|
351
|
+
const entityName = this.extractEntityName(route.operationName);
|
|
352
|
+
return [
|
|
353
|
+
'\tid := c.Params("id")',
|
|
354
|
+
'\tvar item ' + entityName,
|
|
355
|
+
'\t',
|
|
356
|
+
'\tif err := db.First(&item, "id = ?", id).Error; err != nil {',
|
|
357
|
+
'\t\treturn c.Status(404).JSON(fiber.Map{"error": "Not found"})',
|
|
358
|
+
'\t}',
|
|
359
|
+
'\t',
|
|
360
|
+
'\tif err := db.Delete(&item).Error; err != nil {',
|
|
361
|
+
'\t\treturn c.Status(500).JSON(fiber.Map{"error": err.Error()})',
|
|
362
|
+
'\t}',
|
|
363
|
+
'\t',
|
|
364
|
+
'\treturn c.SendStatus(204)',
|
|
365
|
+
];
|
|
366
|
+
}
|
|
367
|
+
extractEntityName(operationName) {
|
|
368
|
+
// Extract entity name from operation name like "listUser" -> "User" or "createPost" -> "Post"
|
|
369
|
+
const match = operationName.match(/(?:list|create|read|update|delete|get)(.+)/i);
|
|
370
|
+
return match ? match[1] : 'Entity';
|
|
371
|
+
}
|
|
372
|
+
// ===========================================================================
|
|
373
|
+
// Model Generation (GORM)
|
|
374
|
+
// ===========================================================================
|
|
375
|
+
generateModels(entities) {
|
|
376
|
+
const lines = [];
|
|
377
|
+
if (this.provenance) {
|
|
378
|
+
lines.push('// Generated by Kappa v2.5 CodeDNA');
|
|
379
|
+
lines.push('// ORM: GORM');
|
|
380
|
+
lines.push('');
|
|
381
|
+
}
|
|
382
|
+
lines.push(`package ${this.packageName}`);
|
|
383
|
+
lines.push('');
|
|
384
|
+
// Collect required imports
|
|
385
|
+
const imports = new Set();
|
|
386
|
+
imports.add('"time"');
|
|
387
|
+
imports.add('"github.com/google/uuid"');
|
|
388
|
+
imports.add('"gorm.io/gorm"');
|
|
389
|
+
imports.add('"encoding/json"');
|
|
390
|
+
lines.push('import (');
|
|
391
|
+
for (const imp of imports) {
|
|
392
|
+
lines.push(`\t${imp}`);
|
|
393
|
+
}
|
|
394
|
+
lines.push(')');
|
|
395
|
+
lines.push('');
|
|
396
|
+
// Suppress unused import warning
|
|
397
|
+
lines.push('var (');
|
|
398
|
+
lines.push('\t_ = time.Now');
|
|
399
|
+
lines.push('\t_ = uuid.New');
|
|
400
|
+
lines.push('\t_ json.RawMessage');
|
|
401
|
+
lines.push(')');
|
|
402
|
+
lines.push('');
|
|
403
|
+
for (const entity of entities) {
|
|
404
|
+
lines.push(this.generateModel(entity));
|
|
405
|
+
lines.push('');
|
|
406
|
+
}
|
|
407
|
+
return lines.join('\n');
|
|
408
|
+
}
|
|
409
|
+
generateModel(entity) {
|
|
410
|
+
const lines = [];
|
|
411
|
+
lines.push(`// ${entity.name} represents the ${entity.name.toLowerCase()} entity`);
|
|
412
|
+
lines.push(`type ${entity.name} struct {`);
|
|
413
|
+
// GORM base model if using UUID
|
|
414
|
+
const hasUUID = entity.fields.some((f) => f.type.kind === 'primitive' && f.type.type === 'uuid' && f.modifiers.includes('primary'));
|
|
415
|
+
if (!hasUUID) {
|
|
416
|
+
lines.push('\tgorm.Model');
|
|
417
|
+
}
|
|
418
|
+
for (const field of entity.fields) {
|
|
419
|
+
lines.push(this.generateModelField(field));
|
|
420
|
+
}
|
|
421
|
+
lines.push('}');
|
|
422
|
+
// TableName method
|
|
423
|
+
lines.push('');
|
|
424
|
+
lines.push(`// TableName returns the table name for ${entity.name}`);
|
|
425
|
+
lines.push(`func (${entity.name}) TableName() string {`);
|
|
426
|
+
lines.push(`\treturn "${this.pluralizeTableName(entity.name)}"`);
|
|
427
|
+
lines.push('}');
|
|
428
|
+
return lines.join('\n');
|
|
429
|
+
}
|
|
430
|
+
generateModelField(field) {
|
|
431
|
+
const goType = this.fieldToGoType(field.type);
|
|
432
|
+
const fieldName = this.toPascalCase(field.name);
|
|
433
|
+
const tags = this.generateFieldTags(field);
|
|
434
|
+
return `\t${fieldName} ${goType} ${tags}`;
|
|
435
|
+
}
|
|
436
|
+
fieldToGoType(type) {
|
|
437
|
+
switch (type.kind) {
|
|
438
|
+
case 'primitive':
|
|
439
|
+
return PRIMITIVE_GO_MAP[type.type] ?? 'string';
|
|
440
|
+
case 'array':
|
|
441
|
+
const elementType = this.fieldToGoType(type.itemType);
|
|
442
|
+
return `[]${elementType}`;
|
|
443
|
+
case 'reference':
|
|
444
|
+
return type.entity;
|
|
445
|
+
case 'enum':
|
|
446
|
+
return 'string';
|
|
447
|
+
default:
|
|
448
|
+
return 'interface{}';
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
generateFieldTags(field) {
|
|
452
|
+
const tags = [];
|
|
453
|
+
// JSON tag
|
|
454
|
+
const jsonName = this.toSnakeCase(field.name);
|
|
455
|
+
tags.push(`json:"${jsonName}"`);
|
|
456
|
+
// GORM tags
|
|
457
|
+
if (this.gorm) {
|
|
458
|
+
const gormParts = [];
|
|
459
|
+
gormParts.push(`column:${jsonName}`);
|
|
460
|
+
if (field.modifiers.includes('primary')) {
|
|
461
|
+
gormParts.push('primaryKey');
|
|
462
|
+
}
|
|
463
|
+
if (field.modifiers.includes('unique')) {
|
|
464
|
+
gormParts.push('uniqueIndex');
|
|
465
|
+
}
|
|
466
|
+
if (!field.modifiers.includes('optional')) {
|
|
467
|
+
gormParts.push('not null');
|
|
468
|
+
}
|
|
469
|
+
// Type-specific GORM tags
|
|
470
|
+
if (field.type.kind === 'primitive') {
|
|
471
|
+
const gormTypeTag = GORM_TAGS[field.type.type];
|
|
472
|
+
if (gormTypeTag) {
|
|
473
|
+
gormParts.push(gormTypeTag);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
tags.push(`gorm:"${gormParts.join(';')}"`);
|
|
477
|
+
}
|
|
478
|
+
// Validation tags
|
|
479
|
+
if (this.validation) {
|
|
480
|
+
const validParts = [];
|
|
481
|
+
if (!field.modifiers.includes('optional')) {
|
|
482
|
+
validParts.push('required');
|
|
483
|
+
}
|
|
484
|
+
if (field.type.kind === 'primitive') {
|
|
485
|
+
if (field.type.type === 'email') {
|
|
486
|
+
validParts.push('email');
|
|
487
|
+
}
|
|
488
|
+
if (field.type.type === 'url') {
|
|
489
|
+
validParts.push('url');
|
|
490
|
+
}
|
|
491
|
+
if (field.type.type === 'uuid') {
|
|
492
|
+
validParts.push('uuid');
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
if (validParts.length > 0) {
|
|
496
|
+
tags.push(`validate:"${validParts.join(',')}"`);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
return '`' + tags.join(' ') + '`';
|
|
500
|
+
}
|
|
501
|
+
// ===========================================================================
|
|
502
|
+
// Request/Response Types
|
|
503
|
+
// ===========================================================================
|
|
504
|
+
generateRequestTypes(apis) {
|
|
505
|
+
const lines = [];
|
|
506
|
+
const routes = this.extractRoutes(apis);
|
|
507
|
+
if (this.provenance) {
|
|
508
|
+
lines.push('// Generated by Kappa v2.5 CodeDNA');
|
|
509
|
+
lines.push('// Request/Response types for Go/Fiber');
|
|
510
|
+
lines.push('');
|
|
511
|
+
}
|
|
512
|
+
lines.push(`package ${this.packageName}`);
|
|
513
|
+
lines.push('');
|
|
514
|
+
for (const route of routes) {
|
|
515
|
+
if (route.parameters.length > 0) {
|
|
516
|
+
lines.push(this.generateRequestType(route));
|
|
517
|
+
lines.push('');
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return lines.join('\n');
|
|
521
|
+
}
|
|
522
|
+
generateRequestType(route) {
|
|
523
|
+
const lines = [];
|
|
524
|
+
const typeName = `${this.toPascalCase(route.operationName)}Request`;
|
|
525
|
+
lines.push(`// ${typeName} represents the request body for ${route.operationName}`);
|
|
526
|
+
lines.push(`type ${typeName} struct {`);
|
|
527
|
+
for (const param of route.parameters) {
|
|
528
|
+
const goType = this.fieldToGoType(param.type);
|
|
529
|
+
const fieldName = this.toPascalCase(param.name);
|
|
530
|
+
const jsonName = this.toSnakeCase(param.name);
|
|
531
|
+
const optional = param.optional ? ',omitempty' : '';
|
|
532
|
+
lines.push(`\t${fieldName} ${goType} \`json:"${jsonName}${optional}"\``);
|
|
533
|
+
}
|
|
534
|
+
lines.push('}');
|
|
535
|
+
return lines.join('\n');
|
|
536
|
+
}
|
|
537
|
+
// ===========================================================================
|
|
538
|
+
// Utility Methods
|
|
539
|
+
// ===========================================================================
|
|
540
|
+
toPascalCase(str) {
|
|
541
|
+
return str
|
|
542
|
+
.replace(/[-_](.)/g, (_, c) => c.toUpperCase())
|
|
543
|
+
.replace(/^(.)/, (_, c) => c.toUpperCase());
|
|
544
|
+
}
|
|
545
|
+
toKebabCase(str) {
|
|
546
|
+
return str
|
|
547
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
548
|
+
.replace(/[_\s]+/g, '-')
|
|
549
|
+
.toLowerCase();
|
|
550
|
+
}
|
|
551
|
+
toSnakeCase(str) {
|
|
552
|
+
return str
|
|
553
|
+
.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
554
|
+
.replace(/[-\s]+/g, '_')
|
|
555
|
+
.toLowerCase();
|
|
556
|
+
}
|
|
557
|
+
pluralizeTableName(entityName) {
|
|
558
|
+
const snakeCase = this.toSnakeCase(entityName);
|
|
559
|
+
// Simple pluralization rules for table names
|
|
560
|
+
// If already plural (ends with 's'), don't add another 's'
|
|
561
|
+
if (snakeCase.endsWith('s')) {
|
|
562
|
+
return snakeCase;
|
|
563
|
+
}
|
|
564
|
+
if (snakeCase.endsWith('x') || snakeCase.endsWith('ch') || snakeCase.endsWith('sh')) {
|
|
565
|
+
return `${snakeCase}es`;
|
|
566
|
+
}
|
|
567
|
+
if (snakeCase.endsWith('y') && !/[aeiou]y$/.test(snakeCase)) {
|
|
568
|
+
return `${snakeCase.slice(0, -1)}ies`;
|
|
569
|
+
}
|
|
570
|
+
return `${snakeCase}s`;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
// =============================================================================
|
|
574
|
+
// Convenience Functions
|
|
575
|
+
// =============================================================================
|
|
576
|
+
export function generateGoFiberAPI(apis, options) {
|
|
577
|
+
const generator = new KappaGoFiberGenerator(options);
|
|
578
|
+
return generator.generateFromAPIs(apis);
|
|
579
|
+
}
|
|
580
|
+
export function generateGoFiberModels(entities, options) {
|
|
581
|
+
const generator = new KappaGoFiberGenerator(options);
|
|
582
|
+
return generator.generateFromEntities(entities).models ?? '';
|
|
583
|
+
}
|
|
584
|
+
export function generateGoFiber(apis, entities, options) {
|
|
585
|
+
const generator = new KappaGoFiberGenerator(options);
|
|
586
|
+
return generator.generateAll(apis, entities);
|
|
587
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { APIBlock, EntityBlock } from './kappa-ast.js';
|
|
2
|
+
export interface LaravelGeneratorOptions {
|
|
3
|
+
/** Namespace for models (default: 'App\\Models') */
|
|
4
|
+
modelNamespace?: string;
|
|
5
|
+
/** Namespace for controllers (default: 'App\\Http\\Controllers') */
|
|
6
|
+
controllerNamespace?: string;
|
|
7
|
+
/** Add provenance comments (default: true) */
|
|
8
|
+
provenance?: boolean;
|
|
9
|
+
/** Generate migrations (default: true) */
|
|
10
|
+
migrations?: boolean;
|
|
11
|
+
/** Generate Form Requests (default: true) */
|
|
12
|
+
formRequests?: boolean;
|
|
13
|
+
/** Use API resources (default: true) */
|
|
14
|
+
apiResources?: boolean;
|
|
15
|
+
/** Base path for routes (default: '/api') */
|
|
16
|
+
basePath?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface GeneratedLaravelFiles {
|
|
19
|
+
/** API routes file */
|
|
20
|
+
routes: string;
|
|
21
|
+
/** Controller files (keyed by controller name) */
|
|
22
|
+
controllers: Record<string, string>;
|
|
23
|
+
/** Model files (keyed by model name) */
|
|
24
|
+
models: Record<string, string>;
|
|
25
|
+
/** Migration files (keyed by migration name) */
|
|
26
|
+
migrations?: Record<string, string>;
|
|
27
|
+
/** Form Request files */
|
|
28
|
+
formRequests?: Record<string, string>;
|
|
29
|
+
/** API Resource files */
|
|
30
|
+
resources?: Record<string, string>;
|
|
31
|
+
}
|
|
32
|
+
export declare class KappaLaravelGenerator {
|
|
33
|
+
private modelNamespace;
|
|
34
|
+
private controllerNamespace;
|
|
35
|
+
private provenance;
|
|
36
|
+
private migrations;
|
|
37
|
+
private formRequests;
|
|
38
|
+
private apiResources;
|
|
39
|
+
private basePath;
|
|
40
|
+
private entities;
|
|
41
|
+
constructor(options?: LaravelGeneratorOptions);
|
|
42
|
+
generateFromAPIs(apis: APIBlock[]): GeneratedLaravelFiles;
|
|
43
|
+
generateFromEntities(entities: EntityBlock[]): GeneratedLaravelFiles;
|
|
44
|
+
generateAll(apis: APIBlock[], entities: EntityBlock[]): GeneratedLaravelFiles;
|
|
45
|
+
private generateRoutes;
|
|
46
|
+
private generateRouteDefinition;
|
|
47
|
+
private extractRoutes;
|
|
48
|
+
private operationToRoute;
|
|
49
|
+
private crudToRoutes;
|
|
50
|
+
private inferMethod;
|
|
51
|
+
private groupByController;
|
|
52
|
+
private generateController;
|
|
53
|
+
private generateControllerMethod;
|
|
54
|
+
private generateModel;
|
|
55
|
+
private generateCasts;
|
|
56
|
+
private generateMigration;
|
|
57
|
+
private generateMigrationColumn;
|
|
58
|
+
private generateFormRequest;
|
|
59
|
+
private generateValidationRules;
|
|
60
|
+
private generateResource;
|
|
61
|
+
private toPascalCase;
|
|
62
|
+
private toCamelCase;
|
|
63
|
+
private toKebabCase;
|
|
64
|
+
private toSnakeCase;
|
|
65
|
+
}
|
|
66
|
+
export declare function generateLaravelAPI(apis: APIBlock[], options?: LaravelGeneratorOptions): GeneratedLaravelFiles;
|
|
67
|
+
export declare function generateLaravelModels(entities: EntityBlock[], options?: LaravelGeneratorOptions): GeneratedLaravelFiles;
|
|
68
|
+
export declare function generateLaravel(apis: APIBlock[], entities: EntityBlock[], options?: LaravelGeneratorOptions): GeneratedLaravelFiles;
|