@auto-engineer/server-generator-apollo-emmett 1.55.0 → 1.57.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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +5 -5
- package/.turbo/turbo-type-check.log +1 -1
- package/CHANGELOG.md +39 -0
- package/dist/src/codegen/scaffoldFromSchema.d.ts +10 -1
- package/dist/src/codegen/scaffoldFromSchema.d.ts.map +1 -1
- package/dist/src/codegen/scaffoldFromSchema.js +38 -9
- package/dist/src/codegen/scaffoldFromSchema.js.map +1 -1
- package/dist/src/codegen/templates/command/commands.specs.ts +1 -1
- package/dist/src/codegen/templates/command/decide.specs.specs.ts +5 -5
- package/dist/src/codegen/templates/command/decide.specs.ts +4 -4
- package/dist/src/codegen/templates/command/events.specs.ts +1 -1
- package/dist/src/codegen/templates/command/evolve.specs.ts +1 -1
- package/dist/src/codegen/templates/command/handle.specs.ts +2 -2
- package/dist/src/codegen/templates/command/mutation.resolver.specs.ts +5 -5
- package/dist/src/codegen/templates/command/register.specs.ts +1 -1
- package/dist/src/codegen/templates/command/state.specs.ts +1 -1
- package/dist/src/codegen/templates/query/projection.specs.specs.ts +11 -11
- package/dist/src/codegen/templates/query/projection.specs.ts +5 -5
- package/dist/src/codegen/templates/query/query.resolver.specs.ts +11 -11
- package/dist/src/codegen/templates/query/state.specs.ts +2 -2
- package/dist/src/codegen/templates/react/react.specs.specs.ts +2 -2
- package/dist/src/codegen/templates/react/react.specs.ts +4 -4
- package/dist/src/codegen/templates/react/react.ts.specs.ts +3 -3
- package/dist/src/codegen/templates/react/register.specs.ts +1 -1
- package/dist/src/commands/generate-server.d.ts +11 -3
- package/dist/src/commands/generate-server.d.ts.map +1 -1
- package/dist/src/commands/generate-server.js +42 -8
- package/dist/src/commands/generate-server.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/codegen/scaffoldFromSchema.filter.specs.ts +66 -2
- package/src/codegen/scaffoldFromSchema.ts +64 -10
- package/src/codegen/templates/command/commands.specs.ts +1 -1
- package/src/codegen/templates/command/decide.specs.specs.ts +5 -5
- package/src/codegen/templates/command/decide.specs.ts +4 -4
- package/src/codegen/templates/command/events.specs.ts +1 -1
- package/src/codegen/templates/command/evolve.specs.ts +1 -1
- package/src/codegen/templates/command/handle.specs.ts +2 -2
- package/src/codegen/templates/command/mutation.resolver.specs.ts +5 -5
- package/src/codegen/templates/command/register.specs.ts +1 -1
- package/src/codegen/templates/command/state.specs.ts +1 -1
- package/src/codegen/templates/query/projection.specs.specs.ts +11 -11
- package/src/codegen/templates/query/projection.specs.ts +5 -5
- package/src/codegen/templates/query/query.resolver.specs.ts +11 -11
- package/src/codegen/templates/query/state.specs.ts +2 -2
- package/src/codegen/templates/react/react.specs.specs.ts +2 -2
- package/src/codegen/templates/react/react.specs.ts +4 -4
- package/src/codegen/templates/react/react.ts.specs.ts +3 -3
- package/src/codegen/templates/react/register.specs.ts +1 -1
- package/src/commands/generate-server.ts +75 -8
|
@@ -65,7 +65,7 @@ describe('query.resolver.ts.ejs', () => {
|
|
|
65
65
|
],
|
|
66
66
|
};
|
|
67
67
|
|
|
68
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
68
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
69
69
|
const resolverFile = plans.find((p) => p.outputPath.endsWith('query.resolver.ts'));
|
|
70
70
|
|
|
71
71
|
expect(resolverFile?.contents).toMatchInlineSnapshot(`
|
|
@@ -191,7 +191,7 @@ describe('query.resolver.ts.ejs', () => {
|
|
|
191
191
|
],
|
|
192
192
|
};
|
|
193
193
|
|
|
194
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
194
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
195
195
|
const resolverFile = plans.find((p) => p.outputPath.endsWith('query.resolver.ts'));
|
|
196
196
|
|
|
197
197
|
expect(resolverFile?.contents).toMatchInlineSnapshot(`
|
|
@@ -359,7 +359,7 @@ describe('query.resolver.ts.ejs', () => {
|
|
|
359
359
|
integrations: [],
|
|
360
360
|
};
|
|
361
361
|
|
|
362
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
362
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
363
363
|
const queryFile = plans.find(
|
|
364
364
|
(p) => p.outputPath.endsWith('query.resolver.ts') && p.contents.includes('ViewsTheQuestionnaireQueryResolver'),
|
|
365
365
|
);
|
|
@@ -489,7 +489,7 @@ describe('query.resolver.ts.ejs', () => {
|
|
|
489
489
|
],
|
|
490
490
|
};
|
|
491
491
|
|
|
492
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
492
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
493
493
|
const resolverFile = plans.find((p) => p.outputPath.endsWith('query.resolver.ts'));
|
|
494
494
|
|
|
495
495
|
expect(resolverFile?.contents).toContain(
|
|
@@ -553,7 +553,7 @@ describe('query.resolver.ts.ejs', () => {
|
|
|
553
553
|
],
|
|
554
554
|
};
|
|
555
555
|
|
|
556
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
556
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
557
557
|
const resolverFile = plans.find((p) => p.outputPath.endsWith('query.resolver.ts'));
|
|
558
558
|
|
|
559
559
|
expect(resolverFile?.contents).toContain('Float');
|
|
@@ -615,7 +615,7 @@ describe('query.resolver.ts.ejs', () => {
|
|
|
615
615
|
],
|
|
616
616
|
};
|
|
617
617
|
|
|
618
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
618
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
619
619
|
const resolverFile = plans.find((p) => p.outputPath.endsWith('query.resolver.ts'));
|
|
620
620
|
|
|
621
621
|
expect(resolverFile?.contents).toContain('Float');
|
|
@@ -684,7 +684,7 @@ describe('query.resolver.ts.ejs', () => {
|
|
|
684
684
|
],
|
|
685
685
|
};
|
|
686
686
|
|
|
687
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
687
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
688
688
|
const resolverFile = plans.find((p) => p.outputPath.endsWith('query.resolver.ts'));
|
|
689
689
|
|
|
690
690
|
// Should return single object, not array
|
|
@@ -757,7 +757,7 @@ describe('query.resolver.ts.ejs', () => {
|
|
|
757
757
|
],
|
|
758
758
|
};
|
|
759
759
|
|
|
760
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
760
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
761
761
|
const resolverFile = plans.find((p) => p.outputPath.endsWith('query.resolver.ts'));
|
|
762
762
|
|
|
763
763
|
expect(resolverFile?.contents).not.toContain("mixedStatus: 'pending'");
|
|
@@ -825,7 +825,7 @@ describe('query.resolver.ts.ejs', () => {
|
|
|
825
825
|
],
|
|
826
826
|
};
|
|
827
827
|
|
|
828
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
828
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
829
829
|
const resolverFile = plans.find((p) => p.outputPath.endsWith('query.resolver.ts'));
|
|
830
830
|
|
|
831
831
|
expect(resolverFile?.contents).toContain('export class WorkoutSessionPerformance');
|
|
@@ -899,7 +899,7 @@ describe('query.resolver.ts.ejs', () => {
|
|
|
899
899
|
],
|
|
900
900
|
};
|
|
901
901
|
|
|
902
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
902
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
903
903
|
const resolverFile = plans.find((p) => p.outputPath.endsWith('query.resolver.ts'));
|
|
904
904
|
|
|
905
905
|
expect(resolverFile?.contents).toMatchInlineSnapshot(`
|
|
@@ -1022,7 +1022,7 @@ describe('query.resolver.ts.ejs', () => {
|
|
|
1022
1022
|
],
|
|
1023
1023
|
};
|
|
1024
1024
|
|
|
1025
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
1025
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
1026
1026
|
const resolverFile = plans.find((p) => p.outputPath.endsWith('query.resolver.ts'));
|
|
1027
1027
|
|
|
1028
1028
|
expect(resolverFile?.contents).toMatchInlineSnapshot(`
|
|
@@ -55,7 +55,7 @@ describe('state.ts.ejs', () => {
|
|
|
55
55
|
],
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
58
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
59
59
|
const stateFile = plans.find((p) => p.outputPath.endsWith('state.ts'));
|
|
60
60
|
|
|
61
61
|
expect(stateFile?.contents).toMatchInlineSnapshot(`
|
|
@@ -93,7 +93,7 @@ describe('state.ts.ejs', () => {
|
|
|
93
93
|
messages: [],
|
|
94
94
|
};
|
|
95
95
|
|
|
96
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
96
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
97
97
|
const stateFile = plans.find((p) => p.outputPath.endsWith('state.ts'));
|
|
98
98
|
|
|
99
99
|
expect(stateFile?.contents).toMatchInlineSnapshot(`
|
|
@@ -171,7 +171,7 @@ describe('react.specs.ts.ejs (react slice)', () => {
|
|
|
171
171
|
],
|
|
172
172
|
};
|
|
173
173
|
|
|
174
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
174
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
175
175
|
|
|
176
176
|
const specFile = plans.find((p) => p.outputPath.endsWith('react.specs.ts'));
|
|
177
177
|
expect(specFile?.contents).toMatchInlineSnapshot(`
|
|
@@ -358,7 +358,7 @@ describe('react.specs.ts.ejs (react slice)', () => {
|
|
|
358
358
|
],
|
|
359
359
|
};
|
|
360
360
|
|
|
361
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
361
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
362
362
|
|
|
363
363
|
const specFile = plans.find((p) => p.outputPath.endsWith('react.specs.ts'));
|
|
364
364
|
expect(specFile?.contents).toBeDefined();
|
|
@@ -229,7 +229,7 @@ describe('handle.ts.ejs (react slice)', () => {
|
|
|
229
229
|
],
|
|
230
230
|
};
|
|
231
231
|
|
|
232
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
232
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
233
233
|
const handleFile = plans.find((p) => p.outputPath.endsWith('react.ts'));
|
|
234
234
|
|
|
235
235
|
expect(handleFile?.contents).toMatchInlineSnapshot(`
|
|
@@ -375,7 +375,7 @@ describe('handle.ts.ejs (react slice)', () => {
|
|
|
375
375
|
],
|
|
376
376
|
};
|
|
377
377
|
|
|
378
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
378
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
379
379
|
const reactFile = plans.find((p) => p.outputPath.endsWith('notify-warehouse/react.ts'));
|
|
380
380
|
|
|
381
381
|
expect(reactFile?.contents).toContain("from '../../order-management/create-order/events'");
|
|
@@ -445,7 +445,7 @@ describe('handle.ts.ejs (react slice)', () => {
|
|
|
445
445
|
],
|
|
446
446
|
};
|
|
447
447
|
|
|
448
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
448
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
449
449
|
const reactFile = plans.find((p) => p.outputPath.endsWith('notify-on-external-event/react.ts'));
|
|
450
450
|
const registerFile = plans.find((p) => p.outputPath.endsWith('notify-on-external-event/register.ts'));
|
|
451
451
|
const specsFile = plans.find((p) => p.outputPath.endsWith('notify-on-external-event/react.specs.ts'));
|
|
@@ -553,7 +553,7 @@ describe('handle.ts.ejs (react slice)', () => {
|
|
|
553
553
|
],
|
|
554
554
|
};
|
|
555
555
|
|
|
556
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
556
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
557
557
|
const reactFile = plans.find((p) => p.outputPath.endsWith('adjust-inventory/react.ts'));
|
|
558
558
|
|
|
559
559
|
expect(reactFile?.contents).toContain('inMemoryReactor<PaymentProcessed>');
|
|
@@ -183,7 +183,7 @@ describe('react.ts.ejs', () => {
|
|
|
183
183
|
],
|
|
184
184
|
};
|
|
185
185
|
|
|
186
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
186
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
187
187
|
const reactFile = plans.find((p) => p.outputPath.endsWith('send-order-confirmation/react.ts'));
|
|
188
188
|
expect(reactFile?.contents).toBeDefined();
|
|
189
189
|
|
|
@@ -276,7 +276,7 @@ describe('react.ts.ejs', () => {
|
|
|
276
276
|
],
|
|
277
277
|
};
|
|
278
278
|
|
|
279
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
279
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
280
280
|
|
|
281
281
|
const reactFiles = plans.filter((p) => p.outputPath.includes('notify-barber-of-cancellation'));
|
|
282
282
|
expect(reactFiles).toEqual([]);
|
|
@@ -394,7 +394,7 @@ describe('react.ts.ejs', () => {
|
|
|
394
394
|
],
|
|
395
395
|
};
|
|
396
396
|
|
|
397
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
397
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
398
398
|
const eventsFile = plans.find((p) => p.outputPath.endsWith('notify-barber-of-new-booking/events.ts'));
|
|
399
399
|
|
|
400
400
|
expect(eventsFile?.contents).toMatchInlineSnapshot(`
|
|
@@ -228,7 +228,7 @@ describe('register.ts.ejs (react slice)', () => {
|
|
|
228
228
|
],
|
|
229
229
|
};
|
|
230
230
|
|
|
231
|
-
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
231
|
+
const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
232
232
|
const registerFile = plans.find((p) => p.outputPath.endsWith('send-notification-to-host/register.ts'));
|
|
233
233
|
|
|
234
234
|
expect(registerFile?.contents).toMatchInlineSnapshot(`
|
|
@@ -7,7 +7,11 @@ import type { Model, Narrative, Slice } from '@auto-engineer/narrative';
|
|
|
7
7
|
import createDebug from 'debug';
|
|
8
8
|
import { execa } from 'execa';
|
|
9
9
|
import fs from 'fs-extra';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
type DuplicateCommandInfo,
|
|
12
|
+
generateScaffoldFilePlans,
|
|
13
|
+
writeScaffoldFilePlans,
|
|
14
|
+
} from '../codegen/scaffoldFromSchema';
|
|
11
15
|
import { ensureDirExists, ensureDirPath, toKebabCase } from '../codegen/utils/path';
|
|
12
16
|
|
|
13
17
|
const debug = createDebug('auto:server-generator-apollo-emmett');
|
|
@@ -88,7 +92,21 @@ export type SliceGeneratedEvent = Event<
|
|
|
88
92
|
}
|
|
89
93
|
>;
|
|
90
94
|
|
|
91
|
-
export type
|
|
95
|
+
export type SliceGenerationFailedEvent = Event<
|
|
96
|
+
'SliceGenerationFailed',
|
|
97
|
+
{
|
|
98
|
+
flowName: string;
|
|
99
|
+
sliceName: string;
|
|
100
|
+
sliceType: string;
|
|
101
|
+
error: string;
|
|
102
|
+
}
|
|
103
|
+
>;
|
|
104
|
+
|
|
105
|
+
export type GenerateServerEvents =
|
|
106
|
+
| ServerGeneratedEvent
|
|
107
|
+
| ServerGenerationFailedEvent
|
|
108
|
+
| SliceGeneratedEvent
|
|
109
|
+
| SliceGenerationFailedEvent;
|
|
92
110
|
|
|
93
111
|
export const commandHandler = defineCommandHandler({
|
|
94
112
|
name: 'GenerateServer',
|
|
@@ -101,6 +119,7 @@ export const commandHandler = defineCommandHandler({
|
|
|
101
119
|
{ name: 'ServerGenerated', displayName: 'Server Generated' },
|
|
102
120
|
{ name: 'ServerGenerationFailed', displayName: 'Server Generation Failed' },
|
|
103
121
|
{ name: 'SliceGenerated', displayName: 'Slice Generated' },
|
|
122
|
+
{ name: 'SliceGenerationFailed', displayName: 'Slice Generation Failed' },
|
|
104
123
|
],
|
|
105
124
|
fields: {
|
|
106
125
|
model: {
|
|
@@ -173,13 +192,17 @@ async function cleanStaleCompiledFiles(serverDir: string): Promise<void> {
|
|
|
173
192
|
debugScaffold(' Cleanup completed');
|
|
174
193
|
}
|
|
175
194
|
|
|
176
|
-
async function generateAndWriteScaffold(
|
|
195
|
+
async function generateAndWriteScaffold(
|
|
196
|
+
spec: Model,
|
|
197
|
+
serverDir: string,
|
|
198
|
+
affectedSliceIds?: Set<string>,
|
|
199
|
+
): Promise<DuplicateCommandInfo[]> {
|
|
177
200
|
const domainFlowsPath = join(serverDir, 'src', 'domain', 'flows');
|
|
178
201
|
debugScaffold('Generating scaffold file plans');
|
|
179
202
|
debugScaffold(' Domain flows path: %s', domainFlowsPath);
|
|
180
203
|
debugScaffold(' Number of flows: %d', spec.narratives?.length || 0);
|
|
181
204
|
|
|
182
|
-
const filePlans = await generateScaffoldFilePlans(
|
|
205
|
+
const { plans: filePlans, duplicateCommands } = await generateScaffoldFilePlans(
|
|
183
206
|
spec.narratives,
|
|
184
207
|
spec.messages,
|
|
185
208
|
spec.integrations,
|
|
@@ -199,6 +222,8 @@ async function generateAndWriteScaffold(spec: Model, serverDir: string, affected
|
|
|
199
222
|
debugScaffold('Written all scaffold files');
|
|
200
223
|
|
|
201
224
|
await cleanStaleCompiledFiles(serverDir);
|
|
225
|
+
|
|
226
|
+
return duplicateCommands;
|
|
202
227
|
}
|
|
203
228
|
|
|
204
229
|
async function copyAllFiles(serverDir: string): Promise<void> {
|
|
@@ -313,11 +338,50 @@ export function createSliceGeneratedEvent(
|
|
|
313
338
|
};
|
|
314
339
|
}
|
|
315
340
|
|
|
341
|
+
export function emitSliceGenerationFailedForDuplicates(
|
|
342
|
+
duplicateCommands: DuplicateCommandInfo[],
|
|
343
|
+
spec: Model,
|
|
344
|
+
command: GenerateServerCommand,
|
|
345
|
+
events: GenerateServerEvents[],
|
|
346
|
+
): void {
|
|
347
|
+
for (const dup of duplicateCommands) {
|
|
348
|
+
for (const flow of spec.narratives) {
|
|
349
|
+
for (const slice of flow.slices) {
|
|
350
|
+
if (slice.type !== 'command') continue;
|
|
351
|
+
const gwtSpecs = slice.server?.specs;
|
|
352
|
+
if (!Array.isArray(gwtSpecs)) continue;
|
|
353
|
+
const hasCommand = gwtSpecs.some((spec) =>
|
|
354
|
+
spec.rules?.some((rule) =>
|
|
355
|
+
rule.examples?.some((ex) => ex.steps?.some((step) => step.keyword === 'When' && step.text === dup.command)),
|
|
356
|
+
),
|
|
357
|
+
);
|
|
358
|
+
if (hasCommand && (flow.name !== dup.existingFlow || slice.name !== dup.existingSlice)) {
|
|
359
|
+
const event: SliceGenerationFailedEvent = {
|
|
360
|
+
type: 'SliceGenerationFailed',
|
|
361
|
+
data: {
|
|
362
|
+
flowName: flow.name,
|
|
363
|
+
sliceName: slice.name,
|
|
364
|
+
sliceType: slice.type,
|
|
365
|
+
error: `Duplicate command handler: '${dup.command}' is already registered in ${dup.existingFlow}/${dup.existingSlice}`,
|
|
366
|
+
},
|
|
367
|
+
timestamp: new Date(),
|
|
368
|
+
requestId: command.requestId,
|
|
369
|
+
correlationId: command.correlationId,
|
|
370
|
+
};
|
|
371
|
+
events.push(event);
|
|
372
|
+
debug('SliceGenerationFailed: %s.%s - duplicate command %s', flow.name, slice.name, dup.command);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
316
379
|
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: nested iteration over model structure is inherent to the task
|
|
317
380
|
export function emitSliceGeneratedForAll(
|
|
318
381
|
spec: Model,
|
|
319
382
|
command: GenerateServerCommand,
|
|
320
383
|
events: GenerateServerEvents[],
|
|
384
|
+
duplicateCommands: DuplicateCommandInfo[] = [],
|
|
321
385
|
): void {
|
|
322
386
|
if (Array.isArray(spec.narratives) && spec.narratives.length > 0) {
|
|
323
387
|
for (const flow of spec.narratives) {
|
|
@@ -330,6 +394,7 @@ export function emitSliceGeneratedForAll(
|
|
|
330
394
|
}
|
|
331
395
|
}
|
|
332
396
|
}
|
|
397
|
+
emitSliceGenerationFailedForDuplicates(duplicateCommands, spec, command, events);
|
|
333
398
|
}
|
|
334
399
|
|
|
335
400
|
export function emitSliceGeneratedForAffected(
|
|
@@ -337,6 +402,7 @@ export function emitSliceGeneratedForAffected(
|
|
|
337
402
|
affectedIds: Set<string>,
|
|
338
403
|
command: GenerateServerCommand,
|
|
339
404
|
events: GenerateServerEvents[],
|
|
405
|
+
duplicateCommands: DuplicateCommandInfo[] = [],
|
|
340
406
|
): void {
|
|
341
407
|
for (const flow of spec.narratives) {
|
|
342
408
|
for (const slice of flow.slices) {
|
|
@@ -348,6 +414,7 @@ export function emitSliceGeneratedForAffected(
|
|
|
348
414
|
}
|
|
349
415
|
}
|
|
350
416
|
}
|
|
417
|
+
emitSliceGenerationFailedForDuplicates(duplicateCommands, spec, command, events);
|
|
351
418
|
}
|
|
352
419
|
|
|
353
420
|
export async function handleGenerateServerCommandInternal(
|
|
@@ -379,8 +446,8 @@ export async function handleGenerateServerCommandInternal(
|
|
|
379
446
|
debug('Full generation mode');
|
|
380
447
|
await cleanServerDir(serverDir);
|
|
381
448
|
await copyAllFiles(serverDir);
|
|
382
|
-
await generateAndWriteScaffold(spec, serverDir);
|
|
383
|
-
emitSliceGeneratedForAll(spec, command, events);
|
|
449
|
+
const duplicateCommands = await generateAndWriteScaffold(spec, serverDir);
|
|
450
|
+
emitSliceGeneratedForAll(spec, command, events, duplicateCommands);
|
|
384
451
|
await writeConfigurationFiles(serverDir, absDest);
|
|
385
452
|
debugDeps('Installing dependencies and generating GraphQL schema...');
|
|
386
453
|
await installDependenciesAndGenerateSchema(serverDir, absDest);
|
|
@@ -399,8 +466,8 @@ export async function handleGenerateServerCommandInternal(
|
|
|
399
466
|
await copyAllFiles(serverDir);
|
|
400
467
|
|
|
401
468
|
const affectedIds = new Set(changeSet.allAffected);
|
|
402
|
-
await generateAndWriteScaffold(spec, serverDir, affectedIds);
|
|
403
|
-
emitSliceGeneratedForAffected(spec, affectedIds, command, events);
|
|
469
|
+
const duplicateCommands = await generateAndWriteScaffold(spec, serverDir, affectedIds);
|
|
470
|
+
emitSliceGeneratedForAffected(spec, affectedIds, command, events, duplicateCommands);
|
|
404
471
|
await writeConfigurationFiles(serverDir, absDest);
|
|
405
472
|
debugDeps('Installing dependencies and generating GraphQL schema...');
|
|
406
473
|
await installDependenciesAndGenerateSchema(serverDir, absDest);
|