@auto-engineer/server-generator-nestjs 1.28.0 → 1.29.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/CHANGELOG.md +44 -0
- package/package.json +3 -3
- package/src/codegen/entity-consolidation.ts +2 -2
- package/src/codegen/extract/graphql.ts +1 -1
- package/src/codegen/scaffoldFromSchema.ts +4 -13
- package/src/codegen/utils/path.ts +3 -3
- package/src/commands/generate-server.ts +25 -74
- package/src/shared/mikro-orm.config.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
# @auto-engineer/server-generator-nestjs
|
|
2
2
|
|
|
3
|
+
## 1.29.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`2ef9632`](https://github.com/BeOnAuto/auto-engineer/commit/2ef9632a2db081f201981422f2ba26b026fa7277) Thanks [@SamHatoum](https://github.com/SamHatoum)! - ### react-component-implementer
|
|
8
|
+
- Add staging workflow with `writeStagedStory`, `checkStagedTypes`, and `promoteFromStaging`
|
|
9
|
+
- Add MCP connection pool and browser pool for resource reuse
|
|
10
|
+
- Add context filtering and system prompt caching to reduce tokens
|
|
11
|
+
- Add ts-service for type-aware error feedback with property suggestions
|
|
12
|
+
- Parallelize type checking and browser validation
|
|
13
|
+
- Replace score-based visual evaluation with binary ACK/NACK criteria
|
|
14
|
+
- Enable incremental TypeScript checking in staging
|
|
15
|
+
|
|
16
|
+
### pipeline
|
|
17
|
+
- Add `.declare().accepts()` builder for AcceptsDescriptor type
|
|
18
|
+
- Show source commands in pipeline graph
|
|
19
|
+
|
|
20
|
+
### job-graph-processor
|
|
21
|
+
- Add `hasFailedJobs` utility and discriminate GraphProcessed vs GraphProcessingFailed
|
|
22
|
+
- Bridge context.emit in command handler for downstream event routing
|
|
23
|
+
|
|
24
|
+
### server-generator-apollo-emmett
|
|
25
|
+
- Add cleanServerDir to preserve node_modules during regeneration
|
|
26
|
+
- Accept Model directly instead of modelPath
|
|
27
|
+
|
|
28
|
+
### server-generator-nestjs
|
|
29
|
+
- Accept Model directly instead of modelPath
|
|
30
|
+
|
|
31
|
+
### generate-react-client
|
|
32
|
+
- Lock in overwrite behavior for copyStarter
|
|
33
|
+
|
|
34
|
+
### narrative
|
|
35
|
+
- Remove export-schema command
|
|
36
|
+
|
|
37
|
+
### global
|
|
38
|
+
- Remove references to information-architect and model-diff packages
|
|
39
|
+
- Fix biome lint errors for CI compliance
|
|
40
|
+
- Update examples and configuration files
|
|
41
|
+
|
|
42
|
+
### Patch Changes
|
|
43
|
+
|
|
44
|
+
- Updated dependencies [[`2ef9632`](https://github.com/BeOnAuto/auto-engineer/commit/2ef9632a2db081f201981422f2ba26b026fa7277)]:
|
|
45
|
+
- @auto-engineer/narrative@1.29.0
|
|
46
|
+
|
|
3
47
|
## 1.28.0
|
|
4
48
|
|
|
5
49
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"ts-node": "^10.9.2",
|
|
41
41
|
"type-fest": "^4.41.0",
|
|
42
42
|
"uuid": "^11.0.0",
|
|
43
|
-
"@auto-engineer/narrative": "1.
|
|
43
|
+
"@auto-engineer/narrative": "1.29.0"
|
|
44
44
|
},
|
|
45
45
|
"publishConfig": {
|
|
46
46
|
"access": "public"
|
|
@@ -52,9 +52,9 @@
|
|
|
52
52
|
"tsx": "^4.19.2",
|
|
53
53
|
"typescript": "^5.8.3",
|
|
54
54
|
"vitest": "^3.2.4",
|
|
55
|
-
"@auto-engineer/cli": "1.
|
|
55
|
+
"@auto-engineer/cli": "1.29.0"
|
|
56
56
|
},
|
|
57
|
-
"version": "1.
|
|
57
|
+
"version": "1.29.0",
|
|
58
58
|
"scripts": {
|
|
59
59
|
"generate:server": "tsx src/cli/index.ts",
|
|
60
60
|
"-DISABLE-build": "tsc && tsx ../../scripts/fix-esm-imports.ts && rm -rf dist/src/codegen/templates && mkdir -p dist/src/codegen && cp -r src/codegen/templates dist/src/codegen/templates && cp -r src/shared dist/src",
|
|
@@ -198,8 +198,8 @@ export function consolidateEntityFields(
|
|
|
198
198
|
const fields = Array.from(fieldMap.values());
|
|
199
199
|
markPrimaryKeyAndIndexes(fields, fieldMap, flow.name);
|
|
200
200
|
|
|
201
|
-
const entityName = pascalCase(flow.name)
|
|
202
|
-
const tableName = camelCase(flow.name)
|
|
201
|
+
const entityName = `${pascalCase(flow.name)}Entity`;
|
|
202
|
+
const tableName = `${camelCase(flow.name)}s`;
|
|
203
203
|
|
|
204
204
|
debug('Consolidated entity: %s with %d fields', entityName, fields.length);
|
|
205
205
|
debug(' Enum imports: %o', Array.from(enumsUsed));
|
|
@@ -88,7 +88,7 @@ export function parseGraphQlRequest(request: string): ParsedGraphQlQuery {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
const baseName = field.name.value;
|
|
91
|
-
const returnType = pascalCase(baseName)
|
|
91
|
+
const returnType = `${pascalCase(baseName)}View`;
|
|
92
92
|
|
|
93
93
|
return {
|
|
94
94
|
queryName: baseName,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path, { dirname } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
1
4
|
import type { Model, Narrative, Slice } from '@auto-engineer/narrative';
|
|
2
5
|
import { camelCase, pascalCase } from 'change-case';
|
|
3
6
|
import createDebug from 'debug';
|
|
4
7
|
import ejs from 'ejs';
|
|
5
|
-
import fs from 'fs/promises';
|
|
6
|
-
import path, { dirname } from 'path';
|
|
7
8
|
import prettier from 'prettier';
|
|
8
|
-
import { fileURLToPath } from 'url';
|
|
9
9
|
import { ensureDirExists, ensureDirPath, toKebabCase } from './utils/path.js';
|
|
10
10
|
|
|
11
11
|
const debug = createDebug('auto:server-generator-nestjs:scaffold');
|
|
@@ -372,7 +372,6 @@ async function generateFilesForSlice(
|
|
|
372
372
|
flow: Narrative,
|
|
373
373
|
sliceDir: string,
|
|
374
374
|
messages: MessageDefinition[],
|
|
375
|
-
flows: Narrative[],
|
|
376
375
|
unionToEnumName: Map<string, string>,
|
|
377
376
|
integrations?: Model['integrations'],
|
|
378
377
|
): Promise<FilePlan[]> {
|
|
@@ -519,15 +518,7 @@ export async function generateScaffoldFilePlans(
|
|
|
519
518
|
|
|
520
519
|
for (const slice of flow.slices) {
|
|
521
520
|
const sliceDir = ensureDirPath(flowDir, toKebabCase(slice.name));
|
|
522
|
-
const plans = await generateFilesForSlice(
|
|
523
|
-
slice,
|
|
524
|
-
flow,
|
|
525
|
-
sliceDir,
|
|
526
|
-
messages ?? [],
|
|
527
|
-
flows,
|
|
528
|
-
unionToEnumName,
|
|
529
|
-
integrations,
|
|
530
|
-
);
|
|
521
|
+
const plans = await generateFilesForSlice(slice, flow, sliceDir, messages ?? [], unionToEnumName, integrations);
|
|
531
522
|
allPlans.push(...plans);
|
|
532
523
|
}
|
|
533
524
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { existsSync } from 'fs';
|
|
2
|
-
import { mkdir } from 'fs/promises';
|
|
3
|
-
import path from 'path';
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { mkdir } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
4
|
|
|
5
5
|
export function toKebabCase(str: string): string {
|
|
6
6
|
return str
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
+
import { writeFile } from 'node:fs/promises';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
1
5
|
import { type Command, defineCommandHandler, type Event } from '@auto-engineer/message-bus';
|
|
2
6
|
import type { Model } from '@auto-engineer/narrative';
|
|
3
7
|
import createDebug from 'debug';
|
|
4
8
|
import { execa } from 'execa';
|
|
5
|
-
import { existsSync } from 'fs';
|
|
6
|
-
import { readFile, writeFile } from 'fs/promises';
|
|
7
9
|
import fs from 'fs-extra';
|
|
8
|
-
import * as path from 'path';
|
|
9
|
-
import { dirname, join, resolve } from 'path';
|
|
10
|
-
import { fileURLToPath } from 'url';
|
|
11
10
|
import { generateScaffoldFilePlans, writeScaffoldFilePlans } from '../codegen/scaffoldFromSchema.js';
|
|
12
11
|
import { ensureDirExists, ensureDirPath, toKebabCase } from '../codegen/utils/path.js';
|
|
13
12
|
|
|
14
13
|
const debug = createDebug('auto:server-generator-nestjs');
|
|
15
|
-
const debugModel = createDebug('auto:server-generator-nestjs:schema');
|
|
16
14
|
const debugFiles = createDebug('auto:server-generator-nestjs:files');
|
|
17
15
|
const debugDeps = createDebug('auto:server-generator-nestjs:deps');
|
|
18
16
|
const debugScaffold = createDebug('auto:server-generator-nestjs:scaffold');
|
|
@@ -23,11 +21,21 @@ const __dirname = dirname(__filename);
|
|
|
23
21
|
export type GenerateServerCommand = Command<
|
|
24
22
|
'GenerateServer',
|
|
25
23
|
{
|
|
26
|
-
|
|
24
|
+
model: Model;
|
|
27
25
|
destination: string;
|
|
28
26
|
}
|
|
29
27
|
>;
|
|
30
28
|
|
|
29
|
+
function deriveModelPath(destination: string): string {
|
|
30
|
+
return join(resolve(destination), '.context', 'schema.json');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function writeModelToDisk(model: Model, destination: string): Promise<void> {
|
|
34
|
+
const modelPath = deriveModelPath(destination);
|
|
35
|
+
await fs.ensureDir(dirname(modelPath));
|
|
36
|
+
await writeFile(modelPath, JSON.stringify(model, null, 2), 'utf8');
|
|
37
|
+
}
|
|
38
|
+
|
|
31
39
|
export type ServerGeneratedEvent = Event<
|
|
32
40
|
'ServerGenerated',
|
|
33
41
|
{
|
|
@@ -68,8 +76,8 @@ export const commandHandler = defineCommandHandler({
|
|
|
68
76
|
icon: 'server',
|
|
69
77
|
events: ['ServerGenerated', 'ServerGenerationFailed', 'SliceGenerated'],
|
|
70
78
|
fields: {
|
|
71
|
-
|
|
72
|
-
description: '
|
|
79
|
+
model: {
|
|
80
|
+
description: 'The model object',
|
|
73
81
|
required: true,
|
|
74
82
|
},
|
|
75
83
|
destination: {
|
|
@@ -77,7 +85,7 @@ export const commandHandler = defineCommandHandler({
|
|
|
77
85
|
required: true,
|
|
78
86
|
},
|
|
79
87
|
},
|
|
80
|
-
examples: ['$ auto generate:server --
|
|
88
|
+
examples: ['$ auto generate:server --destination=.'],
|
|
81
89
|
handle: async (command: Command): Promise<GenerateServerEvents | GenerateServerEvents[]> => {
|
|
82
90
|
const typedCommand = command as GenerateServerCommand;
|
|
83
91
|
const result = await handleGenerateServerCommandInternal(typedCommand);
|
|
@@ -93,58 +101,6 @@ export const commandHandler = defineCommandHandler({
|
|
|
93
101
|
},
|
|
94
102
|
});
|
|
95
103
|
|
|
96
|
-
async function validateModelFile(
|
|
97
|
-
absModel: string,
|
|
98
|
-
command: GenerateServerCommand,
|
|
99
|
-
): Promise<ServerGenerationFailedEvent | null> {
|
|
100
|
-
if (!existsSync(absModel)) {
|
|
101
|
-
debug('Model file not found at %s', absModel);
|
|
102
|
-
return {
|
|
103
|
-
type: 'ServerGenerationFailed',
|
|
104
|
-
data: {
|
|
105
|
-
modelPath: command.data.modelPath,
|
|
106
|
-
destination: command.data.destination,
|
|
107
|
-
error: `Model file not found at ${absModel}`,
|
|
108
|
-
},
|
|
109
|
-
timestamp: new Date(),
|
|
110
|
-
requestId: command.requestId,
|
|
111
|
-
correlationId: command.correlationId,
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
async function readAndParseModel(absModel: string): Promise<Model> {
|
|
118
|
-
debugModel('Reading model file from %s', absModel);
|
|
119
|
-
const content = await readFile(absModel, 'utf8');
|
|
120
|
-
|
|
121
|
-
debugModel('Model content length: %d bytes', content.length);
|
|
122
|
-
const spec = JSON.parse(content) as Model;
|
|
123
|
-
|
|
124
|
-
debugModel('Parsed model:');
|
|
125
|
-
debugModel(' Flows: %d', spec.narratives?.length || 0);
|
|
126
|
-
debugModel(' Messages: %d', spec.messages?.length || 0);
|
|
127
|
-
debugModel(' Integrations: %d', spec.integrations?.length ?? 0);
|
|
128
|
-
|
|
129
|
-
logFlowDetails(spec);
|
|
130
|
-
return spec;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function logFlowDetails(spec: Model): void {
|
|
134
|
-
if (spec.narratives !== undefined && spec.narratives.length > 0) {
|
|
135
|
-
debugModel(
|
|
136
|
-
'Flow names: %o',
|
|
137
|
-
spec.narratives.map((f) => f.name),
|
|
138
|
-
);
|
|
139
|
-
spec.narratives.forEach((flow) => {
|
|
140
|
-
debugModel(' Flow "%s" has %d slices', flow.name, flow.slices?.length || 0);
|
|
141
|
-
flow.slices?.forEach((slice) => {
|
|
142
|
-
debugModel(' Slice: %s (type: %s)', slice.name, slice.type);
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
104
|
async function cleanStaleCompiledFiles(serverDir: string): Promise<void> {
|
|
149
105
|
const sharedTypesDir = join(serverDir, 'src', 'domain', 'shared');
|
|
150
106
|
debugScaffold('Cleaning stale compiled files from %s', sharedTypesDir);
|
|
@@ -233,7 +189,7 @@ function createServerSuccessEvent(
|
|
|
233
189
|
return {
|
|
234
190
|
type: 'ServerGenerated',
|
|
235
191
|
data: {
|
|
236
|
-
modelPath: command.data.
|
|
192
|
+
modelPath: deriveModelPath(command.data.destination),
|
|
237
193
|
destination: command.data.destination,
|
|
238
194
|
serverDir,
|
|
239
195
|
contextSchemaGraphQL: join(absDest, '.context', 'schema.graphql'),
|
|
@@ -249,7 +205,7 @@ function createServerFailureEvent(command: GenerateServerCommand, error: unknown
|
|
|
249
205
|
return {
|
|
250
206
|
type: 'ServerGenerationFailed',
|
|
251
207
|
data: {
|
|
252
|
-
modelPath: command.data.
|
|
208
|
+
modelPath: deriveModelPath(command.data.destination),
|
|
253
209
|
destination: command.data.destination,
|
|
254
210
|
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
255
211
|
},
|
|
@@ -262,29 +218,24 @@ function createServerFailureEvent(command: GenerateServerCommand, error: unknown
|
|
|
262
218
|
export async function handleGenerateServerCommandInternal(
|
|
263
219
|
command: GenerateServerCommand,
|
|
264
220
|
): Promise<GenerateServerEvents[]> {
|
|
265
|
-
const {
|
|
221
|
+
const { model, destination } = command.data;
|
|
266
222
|
const events: GenerateServerEvents[] = [];
|
|
267
223
|
|
|
268
224
|
debug('Starting server generation');
|
|
269
|
-
debug('Model path: %s', modelPath);
|
|
270
225
|
debug('Destination: %s', destination);
|
|
271
226
|
|
|
272
227
|
try {
|
|
273
228
|
const absDest = resolve(destination);
|
|
274
|
-
const absModel = resolve(modelPath);
|
|
275
229
|
|
|
276
230
|
debug('Resolved paths:');
|
|
277
231
|
debug(' Absolute destination: %s', absDest);
|
|
278
|
-
debug(' Absolute model: %s', absModel);
|
|
279
232
|
|
|
280
|
-
const
|
|
281
|
-
if (validationError) return [validationError];
|
|
233
|
+
const spec = model;
|
|
282
234
|
|
|
283
|
-
|
|
235
|
+
await writeModelToDisk(spec, destination);
|
|
284
236
|
|
|
285
237
|
const serverDir = join(absDest, 'server');
|
|
286
238
|
debug('Server directory: %s', serverDir);
|
|
287
|
-
debug('🔄 Generating server... %s', serverDir);
|
|
288
239
|
|
|
289
240
|
debug('Clearing existing server directory if it exists');
|
|
290
241
|
await fs.remove(serverDir);
|
|
@@ -308,7 +259,7 @@ export async function handleGenerateServerCommandInternal(
|
|
|
308
259
|
flowName: flow.name,
|
|
309
260
|
sliceName: slice.name,
|
|
310
261
|
sliceType: slice.type,
|
|
311
|
-
schemaPath: command.data.
|
|
262
|
+
schemaPath: deriveModelPath(command.data.destination),
|
|
312
263
|
slicePath: ensureDirPath('./server/src/domain', toKebabCase(flow.name), toKebabCase(slice.name)),
|
|
313
264
|
},
|
|
314
265
|
timestamp: new Date(),
|
|
@@ -342,7 +293,7 @@ async function copyRootFilesFromSrc(from: string, to: string): Promise<void> {
|
|
|
342
293
|
debugFiles('copyRootFilesFromSrc: from=%s, to=%s', from, to);
|
|
343
294
|
|
|
344
295
|
const fromStat = await fs.stat(from).catch(() => undefined);
|
|
345
|
-
if (fromStat
|
|
296
|
+
if (fromStat?.isFile()) {
|
|
346
297
|
debugFiles(' Source is a file, copying directly');
|
|
347
298
|
await fs.ensureDir(to);
|
|
348
299
|
const destFile = path.join(to, path.basename(from));
|