@auto-engineer/server-generator-apollo-emmett 0.9.13 → 0.10.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/package.json CHANGED
@@ -28,8 +28,8 @@
28
28
  "graphql-type-json": "^0.3.2",
29
29
  "uuid": "^10.0.0",
30
30
  "web-streams-polyfill": "^4.1.0",
31
- "@auto-engineer/flow": "0.9.13",
32
- "@auto-engineer/message-bus": "0.9.13"
31
+ "@auto-engineer/flow": "0.10.0",
32
+ "@auto-engineer/message-bus": "0.10.0"
33
33
  },
34
34
  "publishConfig": {
35
35
  "access": "public"
@@ -37,9 +37,9 @@
37
37
  "devDependencies": {
38
38
  "@types/ejs": "^3.1.5",
39
39
  "@types/fs-extra": "^11.0.4",
40
- "@auto-engineer/cli": "0.9.13"
40
+ "@auto-engineer/cli": "0.10.0"
41
41
  },
42
- "version": "0.9.13",
42
+ "version": "0.10.0",
43
43
  "scripts": {
44
44
  "generate:server": "tsx src/cli/index.ts",
45
45
  "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 src/server.ts dist/src && cp -r src/utils dist/src && cp -r src/domain dist/src",
@@ -5,47 +5,27 @@ import { readFile, writeFile } from 'fs/promises';
5
5
  import { resolve, join } from 'path';
6
6
  import { existsSync } from 'fs';
7
7
  import { generateScaffoldFilePlans, writeScaffoldFilePlans } from '../codegen/scaffoldFromSchema';
8
- import { ensureDirExists } from '../codegen/utils/path';
8
+ import { ensureDirExists, ensureDirPath, toKebabCase } from '../codegen/utils/path';
9
9
  import { Model } from '@auto-engineer/flow';
10
10
  import { fileURLToPath } from 'url';
11
11
  import { dirname } from 'path';
12
12
  import { execa } from 'execa';
13
13
  import createDebug from 'debug';
14
- import { defineCommandHandler } from '@auto-engineer/message-bus';
14
+ import { defineCommandHandler, Command, Event } from '@auto-engineer/message-bus';
15
15
 
16
16
  const debug = createDebug('auto:generate-server');
17
- const debugSchema = createDebug('auto:generate-server:schema');
17
+ const debugModel = createDebug('auto:generate-server:schema');
18
18
  const debugFiles = createDebug('auto:generate-server:files');
19
19
  const debugDeps = createDebug('auto:generate-server:deps');
20
20
  const debugScaffold = createDebug('auto:generate-server:scaffold');
21
21
 
22
- type DefaultRecord = Record<string, unknown>;
23
- export type Command<CommandType extends string = string, CommandData extends DefaultRecord = DefaultRecord> = Readonly<{
24
- type: CommandType;
25
- data: Readonly<CommandData>;
26
- timestamp?: Date;
27
- requestId?: string;
28
- correlationId?: string;
29
- }>;
30
- export type Event<EventType extends string = string, EventData extends DefaultRecord = DefaultRecord> = Readonly<{
31
- type: EventType;
32
- data: Readonly<EventData>;
33
- timestamp?: Date;
34
- requestId?: string;
35
- correlationId?: string;
36
- }>;
37
- export type CommandHandler<TCommand extends Command = Command, TResult = unknown> = {
38
- name: string;
39
- handle: (command: TCommand) => Promise<TResult>;
40
- };
41
-
42
22
  const __filename = fileURLToPath(import.meta.url);
43
23
  const __dirname = dirname(__filename);
44
24
 
45
25
  export type GenerateServerCommand = Command<
46
26
  'GenerateServer',
47
27
  {
48
- schemaPath: string;
28
+ modelPath: string;
49
29
  destination: string; // the project root where "server" directory will be created
50
30
  }
51
31
  >;
@@ -53,7 +33,7 @@ export type GenerateServerCommand = Command<
53
33
  export type ServerGeneratedEvent = Event<
54
34
  'ServerGenerated',
55
35
  {
56
- schemaPath: string;
36
+ modelPath: string;
57
37
  destination: string;
58
38
  serverDir: string;
59
39
  contextSchemaGraphQL?: string;
@@ -63,23 +43,35 @@ export type ServerGeneratedEvent = Event<
63
43
  export type ServerGenerationFailedEvent = Event<
64
44
  'ServerGenerationFailed',
65
45
  {
66
- schemaPath: string;
46
+ modelPath: string;
67
47
  destination: string;
68
48
  error: string;
69
49
  }
70
50
  >;
71
51
 
72
- export type GenerateServerEvents = ServerGeneratedEvent | ServerGenerationFailedEvent;
52
+ export type SliceGeneratedEvent = Event<
53
+ 'SliceGenerated',
54
+ {
55
+ flowName: string;
56
+ sliceName: string;
57
+ sliceType: string;
58
+ schemaPath: string;
59
+ slicePath: string;
60
+ }
61
+ >;
62
+
63
+ export type GenerateServerEvents = ServerGeneratedEvent | ServerGenerationFailedEvent | SliceGeneratedEvent;
73
64
 
74
- export const commandHandler = defineCommandHandler<GenerateServerCommand>({
65
+ export const commandHandler = defineCommandHandler({
75
66
  name: 'GenerateServer',
76
67
  alias: 'generate:server',
77
- description: 'Generate server from schema.json',
68
+ description: 'Generate server from model',
78
69
  category: 'generate',
79
70
  icon: 'server',
71
+ events: ['ServerGenerated', 'ServerGenerationFailed', 'SliceGenerated'],
80
72
  fields: {
81
- schemaPath: {
82
- description: 'Path to schema.json file',
73
+ modelPath: {
74
+ description: 'Path to the json model file',
83
75
  required: true,
84
76
  },
85
77
  destination: {
@@ -87,30 +79,34 @@ export const commandHandler = defineCommandHandler<GenerateServerCommand>({
87
79
  required: true,
88
80
  },
89
81
  },
90
- examples: ['$ auto generate:server --schema-path=.context/schema.json --destination=.'],
91
- handle: async (command: GenerateServerCommand): Promise<ServerGeneratedEvent | ServerGenerationFailedEvent> => {
92
- const result = await handleGenerateServerCommandInternal(command);
93
- if (result.type === 'ServerGenerated') {
94
- debug('Server generated at %s', result.data.serverDir);
95
- } else {
96
- debug('Failed to generate server: %s', result.data.error);
82
+ examples: ['$ auto generate:server --model-path=.context/model.json --destination=.'],
83
+ handle: async (command: Command): Promise<GenerateServerEvents | GenerateServerEvents[]> => {
84
+ const typedCommand = command as GenerateServerCommand;
85
+ const result = await handleGenerateServerCommandInternal(typedCommand);
86
+ const events = Array.isArray(result) ? result : [result];
87
+ const finalEvent = events[events.length - 1];
88
+ if (finalEvent.type === 'ServerGenerated') {
89
+ debug('Server generated at %s', finalEvent.data.serverDir);
90
+ } else if (finalEvent.type === 'ServerGenerationFailed') {
91
+ debug('Failed to generate server: %s', finalEvent.data.error);
97
92
  }
93
+
98
94
  return result;
99
95
  },
100
96
  });
101
97
 
102
- async function validateSchemaFile(
103
- absSchema: string,
98
+ async function validateModelFile(
99
+ absModel: string,
104
100
  command: GenerateServerCommand,
105
101
  ): Promise<ServerGenerationFailedEvent | null> {
106
- if (!existsSync(absSchema)) {
107
- debug('Schema file not found at %s', absSchema);
102
+ if (!existsSync(absModel)) {
103
+ debug('Model file not found at %s', absModel);
108
104
  return {
109
105
  type: 'ServerGenerationFailed',
110
106
  data: {
111
- schemaPath: command.data.schemaPath,
107
+ modelPath: command.data.modelPath,
112
108
  destination: command.data.destination,
113
- error: `Schema file not found at ${absSchema}`,
109
+ error: `Model file not found at ${absModel}`,
114
110
  },
115
111
  timestamp: new Date(),
116
112
  requestId: command.requestId,
@@ -120,17 +116,17 @@ async function validateSchemaFile(
120
116
  return null;
121
117
  }
122
118
 
123
- async function readAndParseSchema(absSchema: string): Promise<Model> {
124
- debugSchema('Reading schema file from %s', absSchema);
125
- const content = await readFile(absSchema, 'utf8');
119
+ async function readAndParseModel(absModel: string): Promise<Model> {
120
+ debugModel('Reading model file from %s', absModel);
121
+ const content = await readFile(absModel, 'utf8');
126
122
 
127
- debugSchema('Schema content length: %d bytes', content.length);
123
+ debugModel('Model content length: %d bytes', content.length);
128
124
  const spec = JSON.parse(content) as Model;
129
125
 
130
- debugSchema('Parsed schema:');
131
- debugSchema(' Flows: %d', spec.flows?.length || 0);
132
- debugSchema(' Messages: %d', spec.messages?.length || 0);
133
- debugSchema(' Integrations: %d', spec.integrations?.length ?? 0);
126
+ debugModel('Parsed model:');
127
+ debugModel(' Flows: %d', spec.flows?.length || 0);
128
+ debugModel(' Messages: %d', spec.messages?.length || 0);
129
+ debugModel(' Integrations: %d', spec.integrations?.length ?? 0);
134
130
 
135
131
  logFlowDetails(spec);
136
132
  return spec;
@@ -138,14 +134,14 @@ async function readAndParseSchema(absSchema: string): Promise<Model> {
138
134
 
139
135
  function logFlowDetails(spec: Model): void {
140
136
  if (spec.flows !== undefined && spec.flows.length > 0) {
141
- debugSchema(
137
+ debugModel(
142
138
  'Flow names: %o',
143
139
  spec.flows.map((f) => f.name),
144
140
  );
145
141
  spec.flows.forEach((flow) => {
146
- debugSchema(' Flow "%s" has %d slices', flow.name, flow.slices?.length || 0);
142
+ debugModel(' Flow "%s" has %d slices', flow.name, flow.slices?.length || 0);
147
143
  flow.slices?.forEach((slice) => {
148
- debugSchema(' Slice: %s (type: %s)', slice.name, slice.type);
144
+ debugModel(' Slice: %s (type: %s)', slice.name, slice.type);
149
145
  });
150
146
  });
151
147
  }
@@ -207,7 +203,7 @@ function createServerSuccessEvent(
207
203
  return {
208
204
  type: 'ServerGenerated',
209
205
  data: {
210
- schemaPath: command.data.schemaPath,
206
+ modelPath: command.data.modelPath,
211
207
  destination: command.data.destination,
212
208
  serverDir,
213
209
  contextSchemaGraphQL: join(absDest, '.context', 'schema.graphql'),
@@ -223,7 +219,7 @@ function createServerFailureEvent(command: GenerateServerCommand, error: unknown
223
219
  return {
224
220
  type: 'ServerGenerationFailed',
225
221
  data: {
226
- schemaPath: command.data.schemaPath,
222
+ modelPath: command.data.modelPath,
227
223
  destination: command.data.destination,
228
224
  error: error instanceof Error ? error.message : 'Unknown error occurred',
229
225
  },
@@ -233,29 +229,28 @@ function createServerFailureEvent(command: GenerateServerCommand, error: unknown
233
229
  };
234
230
  }
235
231
 
236
- async function handleGenerateServerCommandInternal(
232
+ export async function handleGenerateServerCommandInternal(
237
233
  command: GenerateServerCommand,
238
- ): Promise<ServerGeneratedEvent | ServerGenerationFailedEvent> {
239
- const { schemaPath, destination } = command.data;
234
+ ): Promise<GenerateServerEvents[]> {
235
+ const { modelPath, destination } = command.data;
236
+ const events: GenerateServerEvents[] = [];
240
237
 
241
238
  debug('Starting server generation');
242
- debug('Schema path: %s', schemaPath);
239
+ debug('Model path: %s', modelPath);
243
240
  debug('Destination: %s', destination);
244
241
 
245
242
  try {
246
243
  const absDest = resolve(destination);
247
- const absSchema = resolve(schemaPath);
244
+ const absModel = resolve(modelPath);
248
245
 
249
246
  debug('Resolved paths:');
250
247
  debug(' Absolute destination: %s', absDest);
251
- debug(' Absolute schema: %s', absSchema);
248
+ debug(' Absolute model: %s', absModel);
252
249
 
253
- // Validate schema file exists
254
- const validationError = await validateSchemaFile(absSchema, command);
255
- if (validationError) return validationError;
250
+ const validationError = await validateModelFile(absModel, command);
251
+ if (validationError) return [validationError];
256
252
 
257
- // Read and parse schema
258
- const spec = await readAndParseSchema(absSchema);
253
+ const spec = await readAndParseModel(absModel);
259
254
 
260
255
  // Setup server directory
261
256
  const serverDir = join(absDest, 'server');
@@ -265,9 +260,33 @@ async function handleGenerateServerCommandInternal(
265
260
  await ensureDirExists(serverDir);
266
261
  debugFiles('Created server directory: %s', serverDir);
267
262
 
268
- // Generate scaffold files
269
263
  await generateAndWriteScaffold(spec, serverDir);
270
264
 
265
+ if (Array.isArray(spec.flows) && spec.flows.length > 0) {
266
+ for (const flow of spec.flows) {
267
+ if (Array.isArray(flow.slices) && flow.slices.length > 0) {
268
+ for (const slice of flow.slices) {
269
+ if (slice.type === 'experience') continue; // skip experience slices
270
+ const sliceEvent: SliceGeneratedEvent = {
271
+ type: 'SliceGenerated',
272
+ data: {
273
+ flowName: flow.name,
274
+ sliceName: slice.name,
275
+ sliceType: slice.type,
276
+ schemaPath: command.data.modelPath,
277
+ slicePath: ensureDirPath('./server/src/domain/flows', toKebabCase(flow.name), toKebabCase(slice.name)),
278
+ },
279
+ timestamp: new Date(),
280
+ requestId: command.requestId,
281
+ correlationId: command.correlationId,
282
+ };
283
+ events.push(sliceEvent);
284
+ debug('SliceGenerated: %s.%s', flow.name, slice.name);
285
+ }
286
+ }
287
+ }
288
+ }
289
+
271
290
  // Copy files
272
291
  await copyAllFiles(serverDir);
273
292
 
@@ -281,9 +300,13 @@ async function handleGenerateServerCommandInternal(
281
300
  debug('Server generation completed successfully');
282
301
  debug('Server directory: %s', serverDir);
283
302
 
284
- return createServerSuccessEvent(command, serverDir, absDest);
303
+ // Add final success event
304
+ events.push(createServerSuccessEvent(command, serverDir, absDest));
305
+ return events;
285
306
  } catch (error) {
286
- return createServerFailureEvent(command, error);
307
+ // Add failure event to events array if any events were already added
308
+ events.push(createServerFailureEvent(command, error));
309
+ return events;
287
310
  }
288
311
  }
289
312