@auto-engineer/server-generator-apollo-emmett 1.138.0 → 1.140.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 +4 -4
- package/.turbo/turbo-type-check.log +1 -1
- package/CHANGELOG.md +39 -0
- package/DEBUG.md +4 -4
- package/dist/src/codegen/extract/data-sink.d.ts +2 -2
- package/dist/src/codegen/extract/data-sink.d.ts.map +1 -1
- package/dist/src/codegen/extract/data-sink.js +2 -2
- package/dist/src/codegen/extract/data-sink.js.map +1 -1
- package/dist/src/codegen/extract/events.d.ts +3 -3
- package/dist/src/codegen/extract/events.d.ts.map +1 -1
- package/dist/src/codegen/extract/events.js +11 -11
- package/dist/src/codegen/extract/events.js.map +1 -1
- package/dist/src/codegen/extract/gwt.d.ts +2 -2
- package/dist/src/codegen/extract/gwt.d.ts.map +1 -1
- package/dist/src/codegen/extract/gwt.js +2 -2
- package/dist/src/codegen/extract/gwt.js.map +1 -1
- package/dist/src/codegen/extract/imports.d.ts +4 -4
- package/dist/src/codegen/extract/imports.d.ts.map +1 -1
- package/dist/src/codegen/extract/imports.js +8 -8
- package/dist/src/codegen/extract/imports.js.map +1 -1
- package/dist/src/codegen/extract/messages.d.ts +2 -2
- package/dist/src/codegen/extract/messages.d.ts.map +1 -1
- package/dist/src/codegen/extract/messages.js +9 -9
- package/dist/src/codegen/extract/messages.js.map +1 -1
- package/dist/src/codegen/extract/projection.d.ts +7 -7
- package/dist/src/codegen/extract/projection.d.ts.map +1 -1
- package/dist/src/codegen/extract/projection.js +3 -3
- package/dist/src/codegen/extract/projection.js.map +1 -1
- package/dist/src/codegen/extract/query.d.ts +2 -2
- package/dist/src/codegen/extract/query.d.ts.map +1 -1
- package/dist/src/codegen/extract/query.js.map +1 -1
- package/dist/src/codegen/extract/slice-normalizer.d.ts +4 -4
- package/dist/src/codegen/extract/slice-normalizer.d.ts.map +1 -1
- package/dist/src/codegen/extract/slice-normalizer.js +7 -7
- package/dist/src/codegen/extract/slice-normalizer.js.map +1 -1
- package/dist/src/codegen/extract/states.d.ts +3 -3
- package/dist/src/codegen/extract/states.d.ts.map +1 -1
- package/dist/src/codegen/extract/states.js.map +1 -1
- package/dist/src/codegen/extract/step-converter.d.ts +7 -7
- package/dist/src/codegen/extract/step-converter.d.ts.map +1 -1
- package/dist/src/codegen/extract/step-converter.js +12 -12
- package/dist/src/codegen/extract/step-converter.js.map +1 -1
- package/dist/src/codegen/extract/step-types.d.ts +4 -4
- package/dist/src/codegen/extract/step-types.d.ts.map +1 -1
- package/dist/src/codegen/extract/step-types.js +1 -1
- package/dist/src/codegen/extract/step-types.js.map +1 -1
- package/dist/src/codegen/scaffoldFromSchema.d.ts +12 -12
- package/dist/src/codegen/scaffoldFromSchema.d.ts.map +1 -1
- package/dist/src/codegen/scaffoldFromSchema.js +113 -101
- package/dist/src/codegen/scaffoldFromSchema.js.map +1 -1
- package/dist/src/codegen/templates/command/commands.specs.ts +3 -3
- package/dist/src/codegen/templates/command/decide.specs.specs.ts +52 -52
- package/dist/src/codegen/templates/command/decide.specs.ts +12 -12
- package/dist/src/codegen/templates/command/decide.specs.ts.ejs +1 -1
- package/dist/src/codegen/templates/command/events.specs.ts +3 -3
- package/dist/src/codegen/templates/command/evolve.specs.ts +3 -3
- package/dist/src/codegen/templates/command/handle.specs.ts +13 -13
- package/dist/src/codegen/templates/command/mutation.resolver.specs.ts +19 -19
- package/dist/src/codegen/templates/command/register.specs.ts +3 -3
- package/dist/src/codegen/templates/command/state.specs.ts +3 -3
- package/dist/src/codegen/templates/query/events.specs.ts +4 -4
- package/dist/src/codegen/templates/query/projection.specs.specs.ts +60 -60
- package/dist/src/codegen/templates/query/projection.specs.ts +54 -29
- package/dist/src/codegen/templates/query/projection.specs.ts.ejs +2 -2
- package/dist/src/codegen/templates/query/query.resolver.specs.ts +63 -63
- package/dist/src/codegen/templates/query/query.resolver.ts.ejs +1 -1
- package/dist/src/codegen/templates/query/state.specs.ts +9 -9
- package/dist/src/codegen/templates/react/react.specs.specs.ts +15 -15
- package/dist/src/codegen/templates/react/react.specs.ts +16 -16
- package/dist/src/codegen/templates/react/react.specs.ts.ejs +9 -9
- package/dist/src/codegen/templates/react/react.ts.ejs +5 -5
- package/dist/src/codegen/templates/react/react.ts.specs.ts +33 -33
- package/dist/src/codegen/templates/react/register.specs.ts +7 -7
- package/dist/src/codegen/templates/react/register.ts.ejs +4 -4
- package/dist/src/codegen/test-data/specVariant1.d.ts.map +1 -1
- package/dist/src/codegen/test-data/specVariant1.js +3 -2
- package/dist/src/codegen/test-data/specVariant1.js.map +1 -1
- package/dist/src/codegen/types.d.ts +2 -2
- package/dist/src/codegen/types.d.ts.map +1 -1
- package/dist/src/commands/generate-server.d.ts +21 -21
- package/dist/src/commands/generate-server.d.ts.map +1 -1
- package/dist/src/commands/generate-server.js +81 -63
- package/dist/src/commands/generate-server.js.map +1 -1
- package/dist/src/commands/initialize-server.d.ts.map +1 -1
- package/dist/src/commands/initialize-server.js +2 -2
- package/dist/src/commands/initialize-server.js.map +1 -1
- package/dist/src/domain/flows/shared/types.d.ts +14 -0
- package/dist/src/domain/flows/shared/types.d.ts.map +1 -0
- package/dist/src/domain/flows/shared/types.js +2 -0
- package/dist/src/domain/flows/shared/types.js.map +1 -0
- package/dist/src/domain/flows/shared/types.ts +15 -0
- package/dist/src/domain/narratives/shared/types.d.ts +14 -0
- package/dist/src/domain/narratives/shared/types.d.ts.map +1 -0
- package/dist/src/domain/narratives/shared/types.js +2 -0
- package/dist/src/domain/narratives/shared/types.js.map +1 -0
- package/dist/src/domain/narratives/shared/types.ts +15 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/server.js +3 -3
- package/dist/src/server.js.map +1 -1
- package/dist/src/server.ts +3 -3
- package/dist/src/utils/loadRegisterFiles.d.ts +2 -2
- package/dist/src/utils/loadRegisterFiles.d.ts.map +1 -1
- package/dist/src/utils/loadRegisterFiles.js.map +1 -1
- package/dist/src/utils/loadRegisterFiles.ts +5 -5
- package/dist/src/utils/loadResolvers.js +1 -1
- package/dist/src/utils/loadResolvers.js.map +1 -1
- package/dist/src/utils/loadResolvers.ts +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/codegen/extract/data-sink.ts +5 -5
- package/src/codegen/extract/events.ts +15 -15
- package/src/codegen/extract/gwt.ts +4 -4
- package/src/codegen/extract/imports.specs.ts +19 -19
- package/src/codegen/extract/imports.ts +13 -13
- package/src/codegen/extract/messages.specs.ts +30 -30
- package/src/codegen/extract/messages.ts +16 -16
- package/src/codegen/extract/projection.specs.ts +22 -22
- package/src/codegen/extract/projection.ts +9 -9
- package/src/codegen/extract/query.ts +2 -2
- package/src/codegen/extract/slice-normalizer.specs.ts +11 -11
- package/src/codegen/extract/slice-normalizer.ts +14 -14
- package/src/codegen/extract/states.ts +4 -4
- package/src/codegen/extract/step-converter.specs.ts +9 -9
- package/src/codegen/extract/step-converter.ts +15 -15
- package/src/codegen/extract/step-types.specs.ts +12 -12
- package/src/codegen/extract/step-types.ts +4 -4
- package/src/codegen/findEventSource.specs.ts +23 -23
- package/src/codegen/scaffoldErrors.specs.ts +4 -4
- package/src/codegen/scaffoldFromSchema.filter.specs.ts +32 -32
- package/src/codegen/scaffoldFromSchema.ts +146 -132
- package/src/codegen/templates/command/commands.specs.ts +3 -3
- package/src/codegen/templates/command/decide.specs.specs.ts +52 -52
- package/src/codegen/templates/command/decide.specs.ts +12 -12
- package/src/codegen/templates/command/decide.specs.ts.ejs +1 -1
- package/src/codegen/templates/command/events.specs.ts +3 -3
- package/src/codegen/templates/command/evolve.specs.ts +3 -3
- package/src/codegen/templates/command/handle.specs.ts +13 -13
- package/src/codegen/templates/command/mutation.resolver.specs.ts +19 -19
- package/src/codegen/templates/command/register.specs.ts +3 -3
- package/src/codegen/templates/command/state.specs.ts +3 -3
- package/src/codegen/templates/query/events.specs.ts +4 -4
- package/src/codegen/templates/query/projection.specs.specs.ts +60 -60
- package/src/codegen/templates/query/projection.specs.ts +54 -29
- package/src/codegen/templates/query/projection.specs.ts.ejs +2 -2
- package/src/codegen/templates/query/query.resolver.specs.ts +63 -63
- package/src/codegen/templates/query/query.resolver.ts.ejs +1 -1
- package/src/codegen/templates/query/state.specs.ts +9 -9
- package/src/codegen/templates/react/react.specs.specs.ts +15 -15
- package/src/codegen/templates/react/react.specs.ts +16 -16
- package/src/codegen/templates/react/react.specs.ts.ejs +9 -9
- package/src/codegen/templates/react/react.ts.ejs +5 -5
- package/src/codegen/templates/react/react.ts.specs.ts +33 -33
- package/src/codegen/templates/react/register.specs.ts +7 -7
- package/src/codegen/templates/react/register.ts.ejs +4 -4
- package/src/codegen/test-data/specVariant1.json +1 -1
- package/src/codegen/test-data/specVariant1.ts +3 -2
- package/src/codegen/test-data/specVariant2.json +1 -1
- package/src/codegen/types.ts +2 -2
- package/src/commands/generate-server.specs.ts +81 -79
- package/src/commands/generate-server.ts +110 -88
- package/src/commands/initialize-server.specs.ts +4 -4
- package/src/commands/initialize-server.ts +5 -2
- package/src/domain/flows/shared/types.ts +15 -0
- package/src/domain/narratives/shared/types.ts +15 -0
- package/src/index.ts +1 -1
- package/src/server.ts +3 -3
- package/src/utils/loadRegisterFiles.ts +5 -5
- package/src/utils/loadResolvers.ts +1 -1
|
@@ -3,7 +3,7 @@ import * as path from 'node:path';
|
|
|
3
3
|
import { dirname, join, resolve } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import { type Command, defineCommandHandler, type Event } from '@auto-engineer/message-bus';
|
|
6
|
-
import type { Model,
|
|
6
|
+
import type { Model, Moment, Scene } from '@auto-engineer/narrative';
|
|
7
7
|
import createDebug from 'debug';
|
|
8
8
|
|
|
9
9
|
import fs from 'fs-extra';
|
|
@@ -25,7 +25,7 @@ const debugScaffold = createDebug('auto:server-generator-apollo-emmett:scaffold'
|
|
|
25
25
|
const __filename = fileURLToPath(import.meta.url);
|
|
26
26
|
const __dirname = dirname(__filename);
|
|
27
27
|
|
|
28
|
-
type
|
|
28
|
+
type MomentDelta = {
|
|
29
29
|
type: 'added' | 'removed' | 'changed';
|
|
30
30
|
previous?: Record<string, unknown>;
|
|
31
31
|
current?: Record<string, unknown>;
|
|
@@ -37,7 +37,7 @@ export type ChangeSet = {
|
|
|
37
37
|
changed: string[];
|
|
38
38
|
sharedTypesChanged: boolean;
|
|
39
39
|
allAffected: string[];
|
|
40
|
-
deltas: Record<string,
|
|
40
|
+
deltas: Record<string, MomentDelta>;
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
export type GenerationState = {
|
|
@@ -81,29 +81,29 @@ export type ServerGenerationFailedEvent = Event<
|
|
|
81
81
|
destination: string;
|
|
82
82
|
error: string;
|
|
83
83
|
model: Model;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
sceneName?: string;
|
|
85
|
+
momentName?: string;
|
|
86
|
+
momentType?: string;
|
|
87
87
|
}
|
|
88
88
|
>;
|
|
89
89
|
|
|
90
|
-
export type
|
|
91
|
-
'
|
|
90
|
+
export type MomentGeneratedEvent = Event<
|
|
91
|
+
'MomentGenerated',
|
|
92
92
|
{
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
sceneName: string;
|
|
94
|
+
momentName: string;
|
|
95
|
+
momentType: string;
|
|
96
96
|
schemaPath: string;
|
|
97
|
-
|
|
97
|
+
momentPath: string;
|
|
98
98
|
}
|
|
99
99
|
>;
|
|
100
100
|
|
|
101
|
-
export type
|
|
102
|
-
'
|
|
101
|
+
export type MomentGenerationFailedEvent = Event<
|
|
102
|
+
'MomentGenerationFailed',
|
|
103
103
|
{
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
sceneName: string;
|
|
105
|
+
momentName: string;
|
|
106
|
+
momentType: string;
|
|
107
107
|
error: string;
|
|
108
108
|
model: Model;
|
|
109
109
|
}
|
|
@@ -112,8 +112,8 @@ export type SliceGenerationFailedEvent = Event<
|
|
|
112
112
|
export type GenerateServerEvents =
|
|
113
113
|
| ServerGeneratedEvent
|
|
114
114
|
| ServerGenerationFailedEvent
|
|
115
|
-
|
|
|
116
|
-
|
|
|
115
|
+
| MomentGeneratedEvent
|
|
116
|
+
| MomentGenerationFailedEvent;
|
|
117
117
|
|
|
118
118
|
export const commandHandler = defineCommandHandler({
|
|
119
119
|
name: 'GenerateServer',
|
|
@@ -125,8 +125,8 @@ export const commandHandler = defineCommandHandler({
|
|
|
125
125
|
events: [
|
|
126
126
|
{ name: 'ServerGenerated', displayName: 'Back End Built' },
|
|
127
127
|
{ name: 'ServerGenerationFailed', displayName: 'Back End Build Failed' },
|
|
128
|
-
{ name: '
|
|
129
|
-
{ name: '
|
|
128
|
+
{ name: 'MomentGenerated', displayName: 'Moment Generated' },
|
|
129
|
+
{ name: 'MomentGenerationFailed', displayName: 'Moment Generation Failed' },
|
|
130
130
|
],
|
|
131
131
|
fields: {
|
|
132
132
|
model: {
|
|
@@ -202,23 +202,24 @@ async function cleanStaleCompiledFiles(serverDir: string): Promise<void> {
|
|
|
202
202
|
async function generateAndWriteScaffold(
|
|
203
203
|
spec: Model,
|
|
204
204
|
serverDir: string,
|
|
205
|
-
|
|
205
|
+
affectedMomentIds?: Set<string>,
|
|
206
206
|
): Promise<{ duplicateCommands: DuplicateCommandInfo[]; fieldIssues: FieldIssue[] }> {
|
|
207
|
-
const
|
|
207
|
+
const domainNarrativesPath = join(serverDir, 'src', 'domain', 'narratives');
|
|
208
208
|
debugScaffold('Generating scaffold file plans');
|
|
209
|
-
debugScaffold(' Domain
|
|
210
|
-
debugScaffold(' Number of
|
|
209
|
+
debugScaffold(' Domain scenes path: %s', domainNarrativesPath);
|
|
210
|
+
debugScaffold(' Number of scenes: %d', spec.scenes?.length || 0);
|
|
211
211
|
|
|
212
212
|
const {
|
|
213
213
|
plans: filePlans,
|
|
214
214
|
duplicateCommands,
|
|
215
215
|
fieldIssues,
|
|
216
216
|
} = await generateScaffoldFilePlans(
|
|
217
|
-
spec.
|
|
217
|
+
spec.scenes,
|
|
218
218
|
spec.messages,
|
|
219
219
|
spec.integrations,
|
|
220
|
-
|
|
221
|
-
|
|
220
|
+
domainNarrativesPath,
|
|
221
|
+
affectedMomentIds,
|
|
222
|
+
spec.narratives,
|
|
222
223
|
);
|
|
223
224
|
|
|
224
225
|
debugScaffold('Generated %d file plans', filePlans.length);
|
|
@@ -254,7 +255,7 @@ async function copyAllFiles(serverDir: string): Promise<void> {
|
|
|
254
255
|
}
|
|
255
256
|
|
|
256
257
|
export async function writeHealthResolver(serverDir: string): Promise<void> {
|
|
257
|
-
const healthDir = path.join(serverDir, 'src', 'domain', '
|
|
258
|
+
const healthDir = path.join(serverDir, 'src', 'domain', 'narratives', 'health');
|
|
258
259
|
const resolverPath = path.join(healthDir, 'query.resolver.ts');
|
|
259
260
|
await fs.ensureDir(healthDir);
|
|
260
261
|
await writeFile(
|
|
@@ -311,9 +312,9 @@ function stripAnsiCodes(text: string): string {
|
|
|
311
312
|
export function createServerFailureEvent(command: GenerateServerCommand, error: unknown): ServerGenerationFailedEvent {
|
|
312
313
|
debug('Server generation failed with error: %O', error);
|
|
313
314
|
|
|
314
|
-
let
|
|
315
|
-
let
|
|
316
|
-
let
|
|
315
|
+
let sceneName: string | undefined;
|
|
316
|
+
let momentName: string | undefined;
|
|
317
|
+
let momentType: string | undefined;
|
|
317
318
|
let templateFile: string | undefined;
|
|
318
319
|
let errorMessage: string;
|
|
319
320
|
|
|
@@ -321,9 +322,9 @@ export function createServerFailureEvent(command: GenerateServerCommand, error:
|
|
|
321
322
|
let current: unknown = error;
|
|
322
323
|
while (current instanceof Error) {
|
|
323
324
|
if (current instanceof ScaffoldError) {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
325
|
+
sceneName = current.sceneName;
|
|
326
|
+
momentName = current.momentName;
|
|
327
|
+
momentType = current.momentType;
|
|
327
328
|
}
|
|
328
329
|
if (current instanceof TemplateRenderError) {
|
|
329
330
|
templateFile = current.templateFile;
|
|
@@ -345,9 +346,9 @@ export function createServerFailureEvent(command: GenerateServerCommand, error:
|
|
|
345
346
|
destination: command.data.destination,
|
|
346
347
|
error: errorMessage,
|
|
347
348
|
model: command.data.model,
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
349
|
+
sceneName,
|
|
350
|
+
momentName,
|
|
351
|
+
momentType,
|
|
351
352
|
},
|
|
352
353
|
timestamp: new Date(),
|
|
353
354
|
requestId: command.requestId,
|
|
@@ -363,19 +364,21 @@ function findRootCause(error: Error): Error {
|
|
|
363
364
|
return current;
|
|
364
365
|
}
|
|
365
366
|
|
|
366
|
-
export function
|
|
367
|
-
|
|
368
|
-
slice:
|
|
367
|
+
export function createMomentGeneratedEvent(
|
|
368
|
+
scene: Scene,
|
|
369
|
+
slice: Moment,
|
|
369
370
|
command: GenerateServerCommand,
|
|
370
|
-
|
|
371
|
+
sceneDirName?: string,
|
|
372
|
+
): MomentGeneratedEvent {
|
|
373
|
+
const dirName = sceneDirName ?? toKebabCase(scene.name);
|
|
371
374
|
return {
|
|
372
|
-
type: '
|
|
375
|
+
type: 'MomentGenerated',
|
|
373
376
|
data: {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
+
sceneName: scene.name,
|
|
378
|
+
momentName: slice.name,
|
|
379
|
+
momentType: slice.type,
|
|
377
380
|
schemaPath: deriveModelPath(command.data.destination),
|
|
378
|
-
|
|
381
|
+
momentPath: ensureDirPath('./server/src/domain/narratives', dirName, toKebabCase(slice.name)),
|
|
379
382
|
},
|
|
380
383
|
timestamp: new Date(),
|
|
381
384
|
requestId: command.requestId,
|
|
@@ -383,15 +386,30 @@ export function createSliceGeneratedEvent(
|
|
|
383
386
|
};
|
|
384
387
|
}
|
|
385
388
|
|
|
386
|
-
|
|
389
|
+
function buildSceneToNarrativeMap(narratives: Model['narratives']): Map<string, string> {
|
|
390
|
+
const map = new Map<string, string>();
|
|
391
|
+
for (const narrative of narratives) {
|
|
392
|
+
for (const sceneId of narrative.sceneIds) {
|
|
393
|
+
map.set(sceneId, narrative.name);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return map;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function getSceneDirName(scene: Scene, sceneToNarrative: Map<string, string>): string {
|
|
400
|
+
const narrativeName = scene.id ? sceneToNarrative.get(scene.id) : undefined;
|
|
401
|
+
return narrativeName ? `${toKebabCase(narrativeName)}_${toKebabCase(scene.name)}` : toKebabCase(scene.name);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export function emitMomentGenerationFailedForDuplicates(
|
|
387
405
|
duplicateCommands: DuplicateCommandInfo[],
|
|
388
406
|
spec: Model,
|
|
389
407
|
command: GenerateServerCommand,
|
|
390
408
|
events: GenerateServerEvents[],
|
|
391
409
|
): void {
|
|
392
410
|
for (const dup of duplicateCommands) {
|
|
393
|
-
for (const
|
|
394
|
-
for (const slice of
|
|
411
|
+
for (const scene of spec.scenes) {
|
|
412
|
+
for (const slice of scene.moments) {
|
|
395
413
|
if (slice.type !== 'command') continue;
|
|
396
414
|
const gwtSpecs = slice.server?.specs;
|
|
397
415
|
if (!Array.isArray(gwtSpecs)) continue;
|
|
@@ -400,14 +418,14 @@ export function emitSliceGenerationFailedForDuplicates(
|
|
|
400
418
|
rule.examples?.some((ex) => ex.steps?.some((step) => step.keyword === 'When' && step.text === dup.command)),
|
|
401
419
|
),
|
|
402
420
|
);
|
|
403
|
-
if (hasCommand && (
|
|
404
|
-
const event:
|
|
405
|
-
type: '
|
|
421
|
+
if (hasCommand && (scene.name !== dup.existingScene || slice.name !== dup.existingMoment)) {
|
|
422
|
+
const event: MomentGenerationFailedEvent = {
|
|
423
|
+
type: 'MomentGenerationFailed',
|
|
406
424
|
data: {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
error: `Duplicate command handler: '${dup.command}' is already registered in ${dup.
|
|
425
|
+
sceneName: scene.name,
|
|
426
|
+
momentName: slice.name,
|
|
427
|
+
momentType: slice.type,
|
|
428
|
+
error: `Duplicate command handler: '${dup.command}' is already registered in ${dup.existingScene}/${dup.existingMoment}`,
|
|
411
429
|
model: spec,
|
|
412
430
|
},
|
|
413
431
|
timestamp: new Date(),
|
|
@@ -415,7 +433,7 @@ export function emitSliceGenerationFailedForDuplicates(
|
|
|
415
433
|
correlationId: command.correlationId,
|
|
416
434
|
};
|
|
417
435
|
events.push(event);
|
|
418
|
-
debug('
|
|
436
|
+
debug('MomentGenerationFailed: %s.%s - duplicate command %s', scene.name, slice.name, dup.command);
|
|
419
437
|
}
|
|
420
438
|
}
|
|
421
439
|
}
|
|
@@ -423,15 +441,15 @@ export function emitSliceGenerationFailedForDuplicates(
|
|
|
423
441
|
}
|
|
424
442
|
|
|
425
443
|
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: nested iteration over model structure is inherent to the task
|
|
426
|
-
export function
|
|
444
|
+
export function emitMomentGenerationFailedForFieldIssues(
|
|
427
445
|
fieldIssues: FieldIssue[],
|
|
428
446
|
spec: Model,
|
|
429
447
|
command: GenerateServerCommand,
|
|
430
448
|
events: GenerateServerEvents[],
|
|
431
449
|
): void {
|
|
432
450
|
for (const issue of fieldIssues) {
|
|
433
|
-
for (const
|
|
434
|
-
for (const slice of
|
|
451
|
+
for (const scene of spec.scenes) {
|
|
452
|
+
for (const slice of scene.moments) {
|
|
435
453
|
if (slice.type === 'experience') continue;
|
|
436
454
|
|
|
437
455
|
const gwtSpecs = slice.server?.specs;
|
|
@@ -454,11 +472,11 @@ export function emitSliceGenerationFailedForFieldIssues(
|
|
|
454
472
|
|
|
455
473
|
if (usesMessageViaGwt || usesMessageViaData) {
|
|
456
474
|
events.push({
|
|
457
|
-
type: '
|
|
475
|
+
type: 'MomentGenerationFailed',
|
|
458
476
|
data: {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
477
|
+
sceneName: scene.name,
|
|
478
|
+
momentName: slice.name,
|
|
479
|
+
momentType: slice.type,
|
|
462
480
|
error: issue.error,
|
|
463
481
|
model: spec,
|
|
464
482
|
},
|
|
@@ -473,29 +491,31 @@ export function emitSliceGenerationFailedForFieldIssues(
|
|
|
473
491
|
}
|
|
474
492
|
|
|
475
493
|
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: nested iteration over model structure is inherent to the task
|
|
476
|
-
export function
|
|
494
|
+
export function emitMomentGeneratedForAll(
|
|
477
495
|
spec: Model,
|
|
478
496
|
command: GenerateServerCommand,
|
|
479
497
|
events: GenerateServerEvents[],
|
|
480
498
|
duplicateCommands: DuplicateCommandInfo[] = [],
|
|
481
499
|
fieldIssues: FieldIssue[] = [],
|
|
482
500
|
): void {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
501
|
+
const sceneToNarrative = buildSceneToNarrativeMap(spec.narratives);
|
|
502
|
+
if (Array.isArray(spec.scenes) && spec.scenes.length > 0) {
|
|
503
|
+
for (const scene of spec.scenes) {
|
|
504
|
+
const sceneDirName = getSceneDirName(scene, sceneToNarrative);
|
|
505
|
+
if (Array.isArray(scene.moments) && scene.moments.length > 0) {
|
|
506
|
+
for (const slice of scene.moments) {
|
|
487
507
|
if (slice.type === 'experience') continue;
|
|
488
|
-
events.push(
|
|
489
|
-
debug('
|
|
508
|
+
events.push(createMomentGeneratedEvent(scene, slice, command, sceneDirName));
|
|
509
|
+
debug('MomentGenerated: %s.%s', scene.name, slice.name);
|
|
490
510
|
}
|
|
491
511
|
}
|
|
492
512
|
}
|
|
493
513
|
}
|
|
494
|
-
|
|
495
|
-
|
|
514
|
+
emitMomentGenerationFailedForDuplicates(duplicateCommands, spec, command, events);
|
|
515
|
+
emitMomentGenerationFailedForFieldIssues(fieldIssues, spec, command, events);
|
|
496
516
|
}
|
|
497
517
|
|
|
498
|
-
export function
|
|
518
|
+
export function emitMomentGeneratedForAffected(
|
|
499
519
|
spec: Model,
|
|
500
520
|
affectedIds: Set<string>,
|
|
501
521
|
command: GenerateServerCommand,
|
|
@@ -503,18 +523,20 @@ export function emitSliceGeneratedForAffected(
|
|
|
503
523
|
duplicateCommands: DuplicateCommandInfo[] = [],
|
|
504
524
|
fieldIssues: FieldIssue[] = [],
|
|
505
525
|
): void {
|
|
506
|
-
|
|
507
|
-
|
|
526
|
+
const sceneToNarrative = buildSceneToNarrativeMap(spec.narratives);
|
|
527
|
+
for (const scene of spec.scenes) {
|
|
528
|
+
const sceneDirName = getSceneDirName(scene, sceneToNarrative);
|
|
529
|
+
for (const slice of scene.moments) {
|
|
508
530
|
if (slice.type === 'experience') continue;
|
|
509
|
-
const sliceId = `${
|
|
531
|
+
const sliceId = `${sceneDirName}/${toKebabCase(slice.name)}`;
|
|
510
532
|
if (affectedIds.has(sliceId)) {
|
|
511
|
-
events.push(
|
|
512
|
-
debug('
|
|
533
|
+
events.push(createMomentGeneratedEvent(scene, slice, command, sceneDirName));
|
|
534
|
+
debug('MomentGenerated (incremental): %s.%s', scene.name, slice.name);
|
|
513
535
|
}
|
|
514
536
|
}
|
|
515
537
|
}
|
|
516
|
-
|
|
517
|
-
|
|
538
|
+
emitMomentGenerationFailedForDuplicates(duplicateCommands, spec, command, events);
|
|
539
|
+
emitMomentGenerationFailedForFieldIssues(fieldIssues, spec, command, events);
|
|
518
540
|
}
|
|
519
541
|
|
|
520
542
|
export async function handleGenerateServerCommandInternal(
|
|
@@ -547,25 +569,25 @@ export async function handleGenerateServerCommandInternal(
|
|
|
547
569
|
await cleanServerDir(serverDir);
|
|
548
570
|
await copyAllFiles(serverDir);
|
|
549
571
|
const { duplicateCommands, fieldIssues } = await generateAndWriteScaffold(spec, serverDir);
|
|
550
|
-
|
|
572
|
+
emitMomentGeneratedForAll(spec, command, events, duplicateCommands, fieldIssues);
|
|
551
573
|
await writeConfigurationFiles(serverDir);
|
|
552
574
|
} else if (changeSet.allAffected.length === 0 && changeSet.removed.length === 0) {
|
|
553
575
|
debug('No changes detected, skipping scaffold generation');
|
|
554
|
-
|
|
576
|
+
emitMomentGeneratedForAll(spec, command, events);
|
|
555
577
|
} else {
|
|
556
578
|
debug('Incremental generation mode');
|
|
557
579
|
|
|
558
580
|
for (const sliceId of changeSet.removed) {
|
|
559
|
-
const [
|
|
560
|
-
await fs.remove(join(serverDir, 'src/domain/
|
|
561
|
-
debug('Removed slice directory: %s/%s',
|
|
581
|
+
const [sceneDir, momentDir] = sliceId.split('/');
|
|
582
|
+
await fs.remove(join(serverDir, 'src/domain/narratives', sceneDir, momentDir));
|
|
583
|
+
debug('Removed slice directory: %s/%s', sceneDir, momentDir);
|
|
562
584
|
}
|
|
563
585
|
|
|
564
586
|
await copyAllFiles(serverDir);
|
|
565
587
|
|
|
566
588
|
const affectedIds = new Set(changeSet.allAffected);
|
|
567
589
|
const { duplicateCommands, fieldIssues } = await generateAndWriteScaffold(spec, serverDir, affectedIds);
|
|
568
|
-
|
|
590
|
+
emitMomentGeneratedForAffected(spec, affectedIds, command, events, duplicateCommands, fieldIssues);
|
|
569
591
|
await writeConfigurationFiles(serverDir);
|
|
570
592
|
}
|
|
571
593
|
|
|
@@ -41,7 +41,7 @@ describe('handleInitializeServerInternal', () => {
|
|
|
41
41
|
'src/utils/loadResolvers.ts',
|
|
42
42
|
'src/domain/shared/types.ts',
|
|
43
43
|
'src/domain/shared/index.ts',
|
|
44
|
-
'src/domain/
|
|
44
|
+
'src/domain/narratives/health/query.resolver.ts',
|
|
45
45
|
];
|
|
46
46
|
|
|
47
47
|
for (const file of expectedFiles) {
|
|
@@ -79,7 +79,7 @@ describe('handleInitializeServerInternal', () => {
|
|
|
79
79
|
await handleInitializeServerInternal(command);
|
|
80
80
|
|
|
81
81
|
const content = await readFile(
|
|
82
|
-
join(tempDir, 'server', 'src', 'domain', '
|
|
82
|
+
join(tempDir, 'server', 'src', 'domain', 'narratives', 'health', 'query.resolver.ts'),
|
|
83
83
|
'utf8',
|
|
84
84
|
);
|
|
85
85
|
expect(content).toContain('HealthResolver');
|
|
@@ -111,14 +111,14 @@ describe('handleInitializeServerInternal', () => {
|
|
|
111
111
|
const sentinel = 'CUSTOM_CONTENT_SHOULD_NOT_BE_OVERWRITTEN';
|
|
112
112
|
await fs.writeFile(join(serverDir, 'src', 'server.ts'), sentinel);
|
|
113
113
|
await fs.writeFile(join(serverDir, 'src', 'domain', 'shared', 'types.ts'), sentinel);
|
|
114
|
-
await fs.writeFile(join(serverDir, 'src', 'domain', '
|
|
114
|
+
await fs.writeFile(join(serverDir, 'src', 'domain', 'narratives', 'health', 'query.resolver.ts'), sentinel);
|
|
115
115
|
await fs.writeFile(join(serverDir, 'vitest.config.ts'), sentinel);
|
|
116
116
|
|
|
117
117
|
await handleInitializeServerInternal(command);
|
|
118
118
|
|
|
119
119
|
expect(await readFile(join(serverDir, 'src', 'server.ts'), 'utf8')).toBe(sentinel);
|
|
120
120
|
expect(await readFile(join(serverDir, 'src', 'domain', 'shared', 'types.ts'), 'utf8')).toBe(sentinel);
|
|
121
|
-
expect(await readFile(join(serverDir, 'src', 'domain', '
|
|
121
|
+
expect(await readFile(join(serverDir, 'src', 'domain', 'narratives', 'health', 'query.resolver.ts'), 'utf8')).toBe(
|
|
122
122
|
sentinel,
|
|
123
123
|
);
|
|
124
124
|
expect(await readFile(join(serverDir, 'vitest.config.ts'), 'utf8')).toBe(sentinel);
|
|
@@ -242,7 +242,7 @@ async function start() {
|
|
|
242
242
|
schema: { autoMigration: 'CreateOrUpdate' },
|
|
243
243
|
});
|
|
244
244
|
|
|
245
|
-
const resolvers = await loadResolvers('src/domain/
|
|
245
|
+
const resolvers = await loadResolvers('src/domain/narratives/**/*.resolver.{ts,js}');
|
|
246
246
|
type ResolverClass = new (...args: unknown[]) => unknown;
|
|
247
247
|
const schema = await buildSchema({
|
|
248
248
|
resolvers: resolvers as unknown as [ResolverClass, ...ResolverClass[]],
|
|
@@ -367,7 +367,10 @@ export async function handleInitializeServerInternal(
|
|
|
367
367
|
await writeIfMissing(join(serverDir, 'src', 'utils', 'index.ts'), UTILS_INDEX_TS);
|
|
368
368
|
await writeIfMissing(join(serverDir, 'src', 'domain', 'shared', 'types.ts'), TYPES_TS);
|
|
369
369
|
await writeIfMissing(join(serverDir, 'src', 'domain', 'shared', 'index.ts'), SHARED_INDEX_TS);
|
|
370
|
-
await writeIfMissing(
|
|
370
|
+
await writeIfMissing(
|
|
371
|
+
join(serverDir, 'src', 'domain', 'narratives', 'health', 'query.resolver.ts'),
|
|
372
|
+
HEALTH_RESOLVER_TS,
|
|
373
|
+
);
|
|
371
374
|
|
|
372
375
|
return {
|
|
373
376
|
type: 'ServerInitialized',
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import type { CommandSender, EventStore, InMemoryDatabase } from '@event-driven-io/emmett';
|
|
3
|
+
|
|
4
|
+
export interface ReactorContext {
|
|
5
|
+
eventStore: EventStore;
|
|
6
|
+
commandSender: CommandSender;
|
|
7
|
+
database: InMemoryDatabase;
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface GraphQLContext {
|
|
12
|
+
eventStore: EventStore;
|
|
13
|
+
messageBus: CommandSender;
|
|
14
|
+
database: InMemoryDatabase;
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import type { CommandSender, EventStore, InMemoryDatabase } from '@event-driven-io/emmett';
|
|
3
|
+
|
|
4
|
+
export interface ReactorContext {
|
|
5
|
+
eventStore: EventStore;
|
|
6
|
+
commandSender: CommandSender;
|
|
7
|
+
database: InMemoryDatabase;
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface GraphQLContext {
|
|
12
|
+
eventStore: EventStore;
|
|
13
|
+
messageBus: CommandSender;
|
|
14
|
+
database: InMemoryDatabase;
|
|
15
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -5,9 +5,9 @@ export const COMMANDS = [generateServerHandler, initializeServerHandler];
|
|
|
5
5
|
export type {
|
|
6
6
|
GenerateServerCommand,
|
|
7
7
|
GenerateServerEvents,
|
|
8
|
+
MomentGeneratedEvent,
|
|
8
9
|
ServerGeneratedEvent,
|
|
9
10
|
ServerGenerationFailedEvent,
|
|
10
|
-
SliceGeneratedEvent,
|
|
11
11
|
} from './commands/generate-server';
|
|
12
12
|
export type {
|
|
13
13
|
InitializeServerCommand,
|
package/src/server.ts
CHANGED
|
@@ -7,8 +7,8 @@ import { buildSchema } from 'type-graphql';
|
|
|
7
7
|
import { loadProjections, loadRegisterFiles, loadResolvers } from './utils';
|
|
8
8
|
|
|
9
9
|
async function start() {
|
|
10
|
-
const loadedProjections = await loadProjections('src/domain/
|
|
11
|
-
const registrations = await loadRegisterFiles('src/domain/
|
|
10
|
+
const loadedProjections = await loadProjections('src/domain/narratives/**/projection.{ts,js}');
|
|
11
|
+
const registrations = await loadRegisterFiles('src/domain/narratives/**/register.{ts,js}');
|
|
12
12
|
|
|
13
13
|
const messageBus = getInMemoryMessageBus();
|
|
14
14
|
const database = getInMemoryDatabase();
|
|
@@ -63,7 +63,7 @@ async function start() {
|
|
|
63
63
|
};
|
|
64
64
|
process.on('SIGINT', () => void shutdown());
|
|
65
65
|
process.on('SIGTERM', () => void shutdown());
|
|
66
|
-
const resolvers = await loadResolvers('src/domain/
|
|
66
|
+
const resolvers = await loadResolvers('src/domain/narratives/**/*.resolver.{ts,js}');
|
|
67
67
|
type ResolverClass = new (...args: unknown[]) => unknown;
|
|
68
68
|
const schema = await buildSchema({
|
|
69
69
|
resolvers: resolvers as unknown as [ResolverClass, ...ResolverClass[]],
|
|
@@ -2,11 +2,11 @@ import path from 'node:path';
|
|
|
2
2
|
import type { CommandProcessor, EventStore } from '@event-driven-io/emmett';
|
|
3
3
|
import fg from 'fast-glob';
|
|
4
4
|
|
|
5
|
-
export interface
|
|
5
|
+
export interface MomentRegistration {
|
|
6
6
|
register: (messageBus: CommandProcessor, eventStore: EventStore) => Promise<unknown> | undefined;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export async function loadRegisterFiles(source: string): Promise<
|
|
9
|
+
export async function loadRegisterFiles(source: string): Promise<MomentRegistration[]> {
|
|
10
10
|
const files = await fg(source, { absolute: true });
|
|
11
11
|
|
|
12
12
|
const modules = await Promise.all(
|
|
@@ -18,9 +18,9 @@ export async function loadRegisterFiles(source: string): Promise<SliceRegistrati
|
|
|
18
18
|
typeof mod === 'object' &&
|
|
19
19
|
mod !== null &&
|
|
20
20
|
'register' in mod &&
|
|
21
|
-
typeof (mod as
|
|
21
|
+
typeof (mod as MomentRegistration).register === 'function'
|
|
22
22
|
) {
|
|
23
|
-
return mod as
|
|
23
|
+
return mod as MomentRegistration;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
console.warn('⚠️ Skipping invalid register.ts at', file);
|
|
@@ -32,7 +32,7 @@ export async function loadRegisterFiles(source: string): Promise<SliceRegistrati
|
|
|
32
32
|
}),
|
|
33
33
|
);
|
|
34
34
|
|
|
35
|
-
return modules.filter((m): m is
|
|
35
|
+
return modules.filter((m): m is MomentRegistration => m !== null);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
function pathToFileUrl(filePath: string): URL {
|
|
@@ -28,7 +28,7 @@ export async function loadResolvers(source: string): Promise<Resolver[]> {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
if (allResolvers.length === 0) {
|
|
31
|
-
throw new Error('❌ No resolvers found for any
|
|
31
|
+
throw new Error('❌ No resolvers found for any moments.');
|
|
32
32
|
}
|
|
33
33
|
return allResolvers;
|
|
34
34
|
}
|