@barissozen/csns 0.7.5 → 0.7.7
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/README.md +215 -225
- package/dist/agent/index.d.ts +1 -0
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +1 -0
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/tracer/index.d.ts +2 -0
- package/dist/agent/tracer/index.d.ts.map +1 -1
- package/dist/agent/tracer/index.js +1 -0
- package/dist/agent/tracer/index.js.map +1 -1
- package/dist/agent/tracer/security-scanner.d.ts +54 -0
- package/dist/agent/tracer/security-scanner.d.ts.map +1 -0
- package/dist/agent/tracer/security-scanner.js +397 -0
- package/dist/agent/tracer/security-scanner.js.map +1 -0
- package/dist/orchestrator/entity-detector.d.ts +52 -0
- package/dist/orchestrator/entity-detector.d.ts.map +1 -0
- package/dist/orchestrator/entity-detector.js +297 -0
- package/dist/orchestrator/entity-detector.js.map +1 -0
- package/dist/orchestrator/index.d.ts +2 -0
- package/dist/orchestrator/index.d.ts.map +1 -1
- package/dist/orchestrator/index.js +2 -0
- package/dist/orchestrator/index.js.map +1 -1
- package/dist/orchestrator/smart-scaffold.d.ts +36 -0
- package/dist/orchestrator/smart-scaffold.d.ts.map +1 -0
- package/dist/orchestrator/smart-scaffold.js +438 -0
- package/dist/orchestrator/smart-scaffold.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart Scaffold — Brief'ten Multi-Entity Layered Code Generation
|
|
3
|
+
*
|
|
4
|
+
* EntityDetector sonuçlarını alıp her entity için:
|
|
5
|
+
* - Route (Express router + CRUD + relation endpoints)
|
|
6
|
+
* - Service (business logic)
|
|
7
|
+
* - Repository (data access)
|
|
8
|
+
* - Schema (Zod validation)
|
|
9
|
+
* - DB model (Drizzle-style)
|
|
10
|
+
* üretir.
|
|
11
|
+
*/
|
|
12
|
+
import { writeFile, mkdir } from 'node:fs/promises';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
import { EntityDetector } from './entity-detector.js';
|
|
15
|
+
export class SmartScaffold {
|
|
16
|
+
root;
|
|
17
|
+
detector;
|
|
18
|
+
constructor(projectRoot) {
|
|
19
|
+
this.root = projectRoot;
|
|
20
|
+
this.detector = new EntityDetector();
|
|
21
|
+
}
|
|
22
|
+
async generate(brief, decisions) {
|
|
23
|
+
const detection = this.detector.detect(brief);
|
|
24
|
+
const files = [];
|
|
25
|
+
const routes = [];
|
|
26
|
+
// Directories
|
|
27
|
+
const dirs = [
|
|
28
|
+
'src', 'src/routes', 'src/services', 'src/middleware',
|
|
29
|
+
'src/config', 'src/schemas', 'src/models', 'tests',
|
|
30
|
+
...(decisions.database !== 'in-memory' ? ['src/repositories'] : []),
|
|
31
|
+
];
|
|
32
|
+
for (const d of dirs)
|
|
33
|
+
await mkdir(join(this.root, d), { recursive: true });
|
|
34
|
+
// Config
|
|
35
|
+
files.push(await this.writeConfig(decisions));
|
|
36
|
+
// App + server
|
|
37
|
+
files.push(await this.writeApp(detection, decisions));
|
|
38
|
+
files.push(await this.writeServer());
|
|
39
|
+
// Health endpoint
|
|
40
|
+
files.push(await this.writeHealthRoute());
|
|
41
|
+
routes.push('GET /health');
|
|
42
|
+
// Auth (if auth entity exists)
|
|
43
|
+
const authEntity = detection.entities.find(e => e.isAuthEntity);
|
|
44
|
+
if (authEntity && decisions.auth !== 'none') {
|
|
45
|
+
files.push(...await this.writeAuth(authEntity, decisions));
|
|
46
|
+
routes.push('POST /auth/register', 'POST /auth/login', 'GET /auth/me');
|
|
47
|
+
}
|
|
48
|
+
// Each entity → route + service + repo + schema
|
|
49
|
+
for (const entity of detection.entities) {
|
|
50
|
+
if (entity.isAuthEntity)
|
|
51
|
+
continue; // auth handled above
|
|
52
|
+
files.push(await this.writeSchema(entity));
|
|
53
|
+
files.push(await this.writeService(entity, decisions));
|
|
54
|
+
if (decisions.database !== 'in-memory') {
|
|
55
|
+
files.push(await this.writeRepository(entity));
|
|
56
|
+
}
|
|
57
|
+
files.push(await this.writeRoute(entity, detection.relations));
|
|
58
|
+
for (const ep of detection.endpoints.find(e => e.entity === entity.name)?.routes ?? []) {
|
|
59
|
+
routes.push(`${ep.method} ${ep.path}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// DB model (all entities in one file)
|
|
63
|
+
files.push(await this.writeModels(detection));
|
|
64
|
+
return {
|
|
65
|
+
files,
|
|
66
|
+
routes,
|
|
67
|
+
entities: detection.entities.map(e => e.name),
|
|
68
|
+
detection,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// ── Generators ──────────────────────────────────────────
|
|
72
|
+
async writeConfig(d) {
|
|
73
|
+
const path = 'src/config/index.ts';
|
|
74
|
+
const content = `import { z } from 'zod';
|
|
75
|
+
|
|
76
|
+
export const envSchema = z.object({
|
|
77
|
+
PORT: z.coerce.number().default(3000),
|
|
78
|
+
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
|
|
79
|
+
${d.database === 'postgresql' ? " DATABASE_URL: z.string().default('postgresql://localhost:5432/app')," : ''}
|
|
80
|
+
${d.database === 'sqlite' ? " DB_PATH: z.string().default('./data.sqlite')," : ''}
|
|
81
|
+
${d.auth === 'jwt' ? " JWT_SECRET: z.string().min(16, 'JWT_SECRET must be at least 16 characters')," : ''}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
export type Config = z.infer<typeof envSchema>;
|
|
85
|
+
|
|
86
|
+
// Validate at startup — fail fast
|
|
87
|
+
let _config: Config;
|
|
88
|
+
try {
|
|
89
|
+
_config = envSchema.parse(process.env);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.error('❌ Invalid environment configuration:', err);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
export const config = _config;
|
|
95
|
+
`;
|
|
96
|
+
await writeFile(join(this.root, path), content);
|
|
97
|
+
return path;
|
|
98
|
+
}
|
|
99
|
+
async writeApp(det, d) {
|
|
100
|
+
const path = 'src/app.ts';
|
|
101
|
+
const imports = ["import express from 'express';"];
|
|
102
|
+
const mounts = [];
|
|
103
|
+
// Health
|
|
104
|
+
imports.push("import { healthRouter } from './routes/health.js';");
|
|
105
|
+
mounts.push("app.use('/health', healthRouter);");
|
|
106
|
+
// Auth
|
|
107
|
+
const hasAuth = det.entities.some(e => e.isAuthEntity) && d.auth !== 'none';
|
|
108
|
+
if (hasAuth) {
|
|
109
|
+
imports.push("import { authRouter } from './routes/auth.js';");
|
|
110
|
+
imports.push("import { authMiddleware } from './middleware/auth.js';");
|
|
111
|
+
mounts.push("app.use('/auth', authRouter);");
|
|
112
|
+
}
|
|
113
|
+
// Entity routes
|
|
114
|
+
for (const entity of det.entities) {
|
|
115
|
+
if (entity.isAuthEntity)
|
|
116
|
+
continue;
|
|
117
|
+
const varName = `${entity.slug}Router`;
|
|
118
|
+
imports.push(`import { ${varName} } from './routes/${entity.pluralSlug}.js';`);
|
|
119
|
+
if (hasAuth) {
|
|
120
|
+
mounts.push(`app.use('/${entity.pluralSlug}', authMiddleware, ${varName});`);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
mounts.push(`app.use('/${entity.pluralSlug}', ${varName});`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const content = `${imports.join('\n')}
|
|
127
|
+
|
|
128
|
+
export const app = express();
|
|
129
|
+
app.use(express.json());
|
|
130
|
+
|
|
131
|
+
// Routes
|
|
132
|
+
${mounts.join('\n')}
|
|
133
|
+
`;
|
|
134
|
+
await writeFile(join(this.root, path), content);
|
|
135
|
+
return path;
|
|
136
|
+
}
|
|
137
|
+
async writeServer() {
|
|
138
|
+
const path = 'src/server.ts';
|
|
139
|
+
const content = `import { app } from './app.js';
|
|
140
|
+
import { config } from './config/index.js';
|
|
141
|
+
|
|
142
|
+
app.listen(config.PORT, () => {
|
|
143
|
+
console.log(\`Server listening on port \${config.PORT}\`);
|
|
144
|
+
});
|
|
145
|
+
`;
|
|
146
|
+
await writeFile(join(this.root, path), content);
|
|
147
|
+
return path;
|
|
148
|
+
}
|
|
149
|
+
async writeHealthRoute() {
|
|
150
|
+
const path = 'src/routes/health.ts';
|
|
151
|
+
const content = `import { Router } from 'express';
|
|
152
|
+
|
|
153
|
+
export const healthRouter = Router();
|
|
154
|
+
|
|
155
|
+
healthRouter.get('/', (_req, res) => {
|
|
156
|
+
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
|
157
|
+
});
|
|
158
|
+
`;
|
|
159
|
+
await writeFile(join(this.root, path), content);
|
|
160
|
+
return path;
|
|
161
|
+
}
|
|
162
|
+
async writeSchema(entity) {
|
|
163
|
+
const path = `src/schemas/${entity.slug}.ts`;
|
|
164
|
+
const fields = entity.fields
|
|
165
|
+
.filter(f => f.name !== 'userId') // userId comes from auth
|
|
166
|
+
.map(f => {
|
|
167
|
+
let zodType = 'z.string()';
|
|
168
|
+
if (f.type === 'number')
|
|
169
|
+
zodType = 'z.number()';
|
|
170
|
+
else if (f.type === 'boolean')
|
|
171
|
+
zodType = 'z.boolean().default(false)';
|
|
172
|
+
else if (f.type === 'date')
|
|
173
|
+
zodType = 'z.string().datetime().optional()';
|
|
174
|
+
else if (f.type === 'reference')
|
|
175
|
+
zodType = 'z.string().uuid()';
|
|
176
|
+
if (!f.required && f.type !== 'boolean' && f.type !== 'date')
|
|
177
|
+
zodType += '.optional()';
|
|
178
|
+
return ` ${f.name}: ${zodType},`;
|
|
179
|
+
})
|
|
180
|
+
.join('\n');
|
|
181
|
+
const content = `import { z } from 'zod';
|
|
182
|
+
|
|
183
|
+
export const create${entity.name}Schema = z.object({
|
|
184
|
+
${fields}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
export const update${entity.name}Schema = create${entity.name}Schema.partial();
|
|
188
|
+
|
|
189
|
+
export type Create${entity.name}Input = z.infer<typeof create${entity.name}Schema>;
|
|
190
|
+
export type Update${entity.name}Input = z.infer<typeof update${entity.name}Schema>;
|
|
191
|
+
`;
|
|
192
|
+
await writeFile(join(this.root, path), content);
|
|
193
|
+
return path;
|
|
194
|
+
}
|
|
195
|
+
async writeService(entity, d) {
|
|
196
|
+
const path = `src/services/${entity.slug}-service.ts`;
|
|
197
|
+
const usesRepo = d.database !== 'in-memory';
|
|
198
|
+
const E = entity.name;
|
|
199
|
+
const items = entity.pluralSlug;
|
|
200
|
+
const content = usesRepo ? `import { ${E}Repository } from '../repositories/${entity.slug}-repo.js';
|
|
201
|
+
import type { Create${E}Input, Update${E}Input } from '../schemas/${entity.slug}.js';
|
|
202
|
+
|
|
203
|
+
export class ${E}Service {
|
|
204
|
+
private repo = new ${E}Repository();
|
|
205
|
+
|
|
206
|
+
findAll(userId?: string) { return this.repo.findAll(userId); }
|
|
207
|
+
findById(id: string) { return this.repo.findById(id); }
|
|
208
|
+
create(data: Create${E}Input, userId?: string) { return this.repo.create({ ...data, userId }); }
|
|
209
|
+
update(id: string, data: Update${E}Input) { return this.repo.update(id, data); }
|
|
210
|
+
delete(id: string) { return this.repo.delete(id); }
|
|
211
|
+
}
|
|
212
|
+
` : `import type { Create${E}Input, Update${E}Input } from '../schemas/${entity.slug}.js';
|
|
213
|
+
|
|
214
|
+
interface ${E} { id: string; [key: string]: unknown; }
|
|
215
|
+
|
|
216
|
+
export class ${E}Service {
|
|
217
|
+
private ${items}: ${E}[] = [];
|
|
218
|
+
private counter = 0;
|
|
219
|
+
|
|
220
|
+
findAll(userId?: string): ${E}[] {
|
|
221
|
+
return userId ? this.${items}.filter(i => i.userId === userId) : this.${items};
|
|
222
|
+
}
|
|
223
|
+
findById(id: string): ${E} | undefined { return this.${items}.find(i => i.id === id); }
|
|
224
|
+
create(data: Create${E}Input & { userId?: string }): ${E} {
|
|
225
|
+
const item: ${E} = { ...data, id: String(++this.counter) };
|
|
226
|
+
this.${items}.push(item);
|
|
227
|
+
return item;
|
|
228
|
+
}
|
|
229
|
+
update(id: string, data: Update${E}Input): ${E} | undefined {
|
|
230
|
+
const idx = this.${items}.findIndex(i => i.id === id);
|
|
231
|
+
if (idx < 0) return undefined;
|
|
232
|
+
this.${items}[idx] = { ...this.${items}[idx]!, ...data, id };
|
|
233
|
+
return this.${items}[idx];
|
|
234
|
+
}
|
|
235
|
+
delete(id: string): boolean {
|
|
236
|
+
const idx = this.${items}.findIndex(i => i.id === id);
|
|
237
|
+
if (idx < 0) return false;
|
|
238
|
+
this.${items}.splice(idx, 1);
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
`;
|
|
243
|
+
await writeFile(join(this.root, path), content);
|
|
244
|
+
return path;
|
|
245
|
+
}
|
|
246
|
+
async writeRepository(entity) {
|
|
247
|
+
const path = `src/repositories/${entity.slug}-repo.ts`;
|
|
248
|
+
const E = entity.name;
|
|
249
|
+
const content = `/**
|
|
250
|
+
* ${E} Repository — data access layer
|
|
251
|
+
* TODO: Replace with real database queries
|
|
252
|
+
*/
|
|
253
|
+
|
|
254
|
+
interface ${E} { id: string; [key: string]: unknown; }
|
|
255
|
+
|
|
256
|
+
export class ${E}Repository {
|
|
257
|
+
private items: ${E}[] = [];
|
|
258
|
+
private counter = 0;
|
|
259
|
+
|
|
260
|
+
findAll(userId?: string): ${E}[] {
|
|
261
|
+
return userId ? this.items.filter(i => i.userId === userId) : this.items;
|
|
262
|
+
}
|
|
263
|
+
findById(id: string): ${E} | undefined { return this.items.find(i => i.id === id); }
|
|
264
|
+
create(data: Record<string, unknown>): ${E} {
|
|
265
|
+
const item = { ...data, id: String(++this.counter) } as ${E};
|
|
266
|
+
this.items.push(item);
|
|
267
|
+
return item;
|
|
268
|
+
}
|
|
269
|
+
update(id: string, data: Record<string, unknown>): ${E} | undefined {
|
|
270
|
+
const idx = this.items.findIndex(i => i.id === id);
|
|
271
|
+
if (idx < 0) return undefined;
|
|
272
|
+
this.items[idx] = { ...this.items[idx]!, ...data, id };
|
|
273
|
+
return this.items[idx];
|
|
274
|
+
}
|
|
275
|
+
delete(id: string): boolean {
|
|
276
|
+
const idx = this.items.findIndex(i => i.id === id);
|
|
277
|
+
if (idx < 0) return false;
|
|
278
|
+
this.items.splice(idx, 1);
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
`;
|
|
283
|
+
await writeFile(join(this.root, path), content);
|
|
284
|
+
return path;
|
|
285
|
+
}
|
|
286
|
+
async writeRoute(entity, _relations) {
|
|
287
|
+
const path = `src/routes/${entity.pluralSlug}.ts`;
|
|
288
|
+
const E = entity.name;
|
|
289
|
+
const service = `${entity.slug}Service`;
|
|
290
|
+
const validation = `create${E}Schema`;
|
|
291
|
+
const updateValidation = `update${E}Schema`;
|
|
292
|
+
let content = `import { Router } from 'express';
|
|
293
|
+
import { ${E}Service } from '../services/${entity.slug}-service.js';
|
|
294
|
+
import { ${validation}, ${updateValidation} } from '../schemas/${entity.slug}.js';
|
|
295
|
+
|
|
296
|
+
export const ${entity.slug}Router = Router();
|
|
297
|
+
const ${service} = new ${E}Service();
|
|
298
|
+
|
|
299
|
+
// GET /${entity.pluralSlug}
|
|
300
|
+
${entity.slug}Router.get('/', (req, res) => {
|
|
301
|
+
const userId = (req as any).userId; // from auth middleware
|
|
302
|
+
const items = ${service}.findAll(userId);
|
|
303
|
+
res.json(items);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// GET /${entity.pluralSlug}/:id
|
|
307
|
+
${entity.slug}Router.get('/:id', (req, res) => {
|
|
308
|
+
const item = ${service}.findById(req.params.id!);
|
|
309
|
+
if (!item) { res.status(404).json({ error: '${E} not found' }); return; }
|
|
310
|
+
res.json(item);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// POST /${entity.pluralSlug}
|
|
314
|
+
${entity.slug}Router.post('/', (req, res) => {
|
|
315
|
+
const parsed = ${validation}.safeParse(req.body);
|
|
316
|
+
if (!parsed.success) { res.status(400).json({ error: parsed.error.flatten() }); return; }
|
|
317
|
+
const userId = (req as any).userId;
|
|
318
|
+
const item = ${service}.create(parsed.data, userId);
|
|
319
|
+
res.status(201).json(item);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
// PUT /${entity.pluralSlug}/:id
|
|
323
|
+
${entity.slug}Router.put('/:id', (req, res) => {
|
|
324
|
+
const parsed = ${updateValidation}.safeParse(req.body);
|
|
325
|
+
if (!parsed.success) { res.status(400).json({ error: parsed.error.flatten() }); return; }
|
|
326
|
+
const item = ${service}.update(req.params.id!, parsed.data);
|
|
327
|
+
if (!item) { res.status(404).json({ error: '${E} not found' }); return; }
|
|
328
|
+
res.json(item);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// DELETE /${entity.pluralSlug}/:id
|
|
332
|
+
${entity.slug}Router.delete('/:id', (req, res) => {
|
|
333
|
+
const ok = ${service}.delete(req.params.id!);
|
|
334
|
+
if (!ok) { res.status(404).json({ error: '${E} not found' }); return; }
|
|
335
|
+
res.status(204).end();
|
|
336
|
+
});
|
|
337
|
+
`;
|
|
338
|
+
await writeFile(join(this.root, path), content);
|
|
339
|
+
return path;
|
|
340
|
+
}
|
|
341
|
+
async writeAuth(_entity, _d) {
|
|
342
|
+
const files = [];
|
|
343
|
+
// Auth route
|
|
344
|
+
const routeContent = `import { Router } from 'express';
|
|
345
|
+
import { z } from 'zod';
|
|
346
|
+
|
|
347
|
+
export const authRouter = Router();
|
|
348
|
+
|
|
349
|
+
const registerSchema = z.object({
|
|
350
|
+
email: z.string().email(),
|
|
351
|
+
password: z.string().min(8),
|
|
352
|
+
name: z.string().min(1),
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
const loginSchema = z.object({
|
|
356
|
+
email: z.string().email(),
|
|
357
|
+
password: z.string(),
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// In-memory store (replace with real DB)
|
|
361
|
+
const users: Array<{ id: string; email: string; password: string; name: string }> = [];
|
|
362
|
+
let counter = 0;
|
|
363
|
+
|
|
364
|
+
authRouter.post('/register', (req, res) => {
|
|
365
|
+
const parsed = registerSchema.safeParse(req.body);
|
|
366
|
+
if (!parsed.success) { res.status(400).json({ error: parsed.error.flatten() }); return; }
|
|
367
|
+
const exists = users.find(u => u.email === parsed.data.email);
|
|
368
|
+
if (exists) { res.status(409).json({ error: 'Email already registered' }); return; }
|
|
369
|
+
const user = { id: String(++counter), ...parsed.data };
|
|
370
|
+
users.push(user);
|
|
371
|
+
res.status(201).json({ id: user.id, email: user.email, name: user.name });
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
authRouter.post('/login', (req, res) => {
|
|
375
|
+
const parsed = loginSchema.safeParse(req.body);
|
|
376
|
+
if (!parsed.success) { res.status(400).json({ error: parsed.error.flatten() }); return; }
|
|
377
|
+
const user = users.find(u => u.email === parsed.data.email && u.password === parsed.data.password);
|
|
378
|
+
if (!user) { res.status(401).json({ error: 'Invalid credentials' }); return; }
|
|
379
|
+
res.json({ token: \`token-\${user.id}-\${Date.now()}\`, userId: user.id });
|
|
380
|
+
});
|
|
381
|
+
`;
|
|
382
|
+
await writeFile(join(this.root, 'src/routes/auth.ts'), routeContent);
|
|
383
|
+
files.push('src/routes/auth.ts');
|
|
384
|
+
// Auth middleware
|
|
385
|
+
const middlewareContent = `import type { Request, Response, NextFunction } from 'express';
|
|
386
|
+
|
|
387
|
+
export function authMiddleware(req: Request, res: Response, next: NextFunction): void {
|
|
388
|
+
const token = req.headers.authorization?.replace('Bearer ', '');
|
|
389
|
+
if (!token) { res.status(401).json({ error: 'Authentication required' }); return; }
|
|
390
|
+
// TODO: Validate token properly (JWT verify)
|
|
391
|
+
const userId = token.split('-')[1]; // extract from token-{userId}-{timestamp}
|
|
392
|
+
(req as any).userId = userId;
|
|
393
|
+
next();
|
|
394
|
+
}
|
|
395
|
+
`;
|
|
396
|
+
await writeFile(join(this.root, 'src/middleware/auth.ts'), middlewareContent);
|
|
397
|
+
files.push('src/middleware/auth.ts');
|
|
398
|
+
return files;
|
|
399
|
+
}
|
|
400
|
+
async writeModels(det) {
|
|
401
|
+
const path = 'src/models/schema.ts';
|
|
402
|
+
const tables = det.entities.map(entity => {
|
|
403
|
+
const fields = entity.fields.map(f => {
|
|
404
|
+
const colType = f.type === 'number' ? 'integer' :
|
|
405
|
+
f.type === 'boolean' ? 'boolean' :
|
|
406
|
+
f.type === 'date' ? 'timestamp' :
|
|
407
|
+
f.type === 'reference' ? `text /* FK → ${f.reference} */` :
|
|
408
|
+
'text';
|
|
409
|
+
return ` ${f.name}: ${colType}('${f.name}')${f.required ? '.notNull()' : ''},`;
|
|
410
|
+
}).join('\n');
|
|
411
|
+
return `// ${entity.name}
|
|
412
|
+
export const ${entity.pluralSlug} = {
|
|
413
|
+
tableName: '${entity.pluralSlug}',
|
|
414
|
+
columns: {
|
|
415
|
+
id: 'text("id").primaryKey()',
|
|
416
|
+
${fields}
|
|
417
|
+
createdAt: 'timestamp("created_at").defaultNow()',
|
|
418
|
+
updatedAt: 'timestamp("updated_at").defaultNow()',
|
|
419
|
+
},
|
|
420
|
+
};`;
|
|
421
|
+
}).join('\n\n');
|
|
422
|
+
const relSummary = det.relations.map(r => r.from + ' → ' + r.to + ' (' + r.type + ')').join(', ') || 'none';
|
|
423
|
+
const entitySummary = det.entities.map(e => e.name).join(', ');
|
|
424
|
+
const content = `/**
|
|
425
|
+
* Database Schema — All entities
|
|
426
|
+
* Generated by CSNS Smart Scaffold
|
|
427
|
+
*
|
|
428
|
+
* Entities: ${entitySummary}
|
|
429
|
+
* Relations: ${relSummary}
|
|
430
|
+
*/
|
|
431
|
+
|
|
432
|
+
${tables}
|
|
433
|
+
`;
|
|
434
|
+
await writeFile(join(this.root, path), content);
|
|
435
|
+
return path;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
//# sourceMappingURL=smart-scaffold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smart-scaffold.js","sourceRoot":"","sources":["../../src/orchestrator/smart-scaffold.ts"],"names":[],"mappings":"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"}
|