@auto-engineer/server-generator-apollo-emmett 1.104.0 → 1.105.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.
Files changed (41) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-test.log +6 -6
  3. package/.turbo/turbo-type-check.log +1 -1
  4. package/CHANGELOG.md +66 -0
  5. package/dist/src/codegen/scaffoldFromSchema.d.ts +10 -0
  6. package/dist/src/codegen/scaffoldFromSchema.d.ts.map +1 -1
  7. package/dist/src/codegen/scaffoldFromSchema.js +55 -20
  8. package/dist/src/codegen/scaffoldFromSchema.js.map +1 -1
  9. package/dist/src/codegen/templates/command/decide.specs.ts +16 -4
  10. package/dist/src/codegen/templates/command/decide.ts.ejs +4 -1
  11. package/dist/src/codegen/templates/command/evolve.specs.ts +16 -15
  12. package/dist/src/codegen/templates/command/evolve.ts.ejs +16 -15
  13. package/dist/src/codegen/templates/command/handle.specs.ts +146 -0
  14. package/dist/src/codegen/templates/command/handle.ts.ejs +21 -1
  15. package/dist/src/codegen/templates/query/projection.specs.ts +4 -1
  16. package/dist/src/codegen/templates/query/projection.ts.ejs +7 -1
  17. package/dist/src/codegen/templates/react/react.specs.specs.ts +0 -32
  18. package/dist/src/codegen/templates/react/react.ts.specs.ts +0 -49
  19. package/dist/src/commands/generate-server.d.ts +4 -0
  20. package/dist/src/commands/generate-server.d.ts.map +1 -1
  21. package/dist/src/commands/generate-server.js +37 -3
  22. package/dist/src/commands/generate-server.js.map +1 -1
  23. package/dist/tsconfig.tsbuildinfo +1 -1
  24. package/ketchup-plan.md +2 -0
  25. package/package.json +4 -4
  26. package/src/codegen/formatTsValue.specs.ts +12 -0
  27. package/src/codegen/formatTsValueSimple.specs.ts +1 -1
  28. package/src/codegen/scaffoldErrors.specs.ts +40 -0
  29. package/src/codegen/scaffoldFromSchema.ts +70 -31
  30. package/src/codegen/templates/command/decide.specs.ts +16 -4
  31. package/src/codegen/templates/command/decide.ts.ejs +4 -1
  32. package/src/codegen/templates/command/evolve.specs.ts +16 -15
  33. package/src/codegen/templates/command/evolve.ts.ejs +16 -15
  34. package/src/codegen/templates/command/handle.specs.ts +146 -0
  35. package/src/codegen/templates/command/handle.ts.ejs +21 -1
  36. package/src/codegen/templates/query/projection.specs.ts +4 -1
  37. package/src/codegen/templates/query/projection.ts.ejs +7 -1
  38. package/src/codegen/templates/react/react.specs.specs.ts +0 -32
  39. package/src/codegen/templates/react/react.ts.specs.ts +0 -49
  40. package/src/commands/generate-server.specs.ts +71 -0
  41. package/src/commands/generate-server.ts +45 -2
@@ -72,14 +72,6 @@ describe('react.specs.ts.ejs (react slice)', () => {
72
72
  name: 'Send notification to host',
73
73
  server: {
74
74
  description: 'Sends a host notification command in response to BookingRequested',
75
- data: {
76
- items: [
77
- {
78
- target: { type: 'Command', name: 'NotifyHost' },
79
- destination: { type: 'stream', pattern: 'booking-${hostId}' },
80
- },
81
- ],
82
- },
83
75
  specs: [
84
76
  {
85
77
  type: 'gherkin',
@@ -291,14 +283,6 @@ describe('react.specs.ts.ejs (react slice)', () => {
291
283
  name: 'Process tip after completion',
292
284
  server: {
293
285
  description: 'Processes tip command after appointment completion',
294
- data: {
295
- items: [
296
- {
297
- target: { type: 'Command', name: 'ProcessTip' },
298
- destination: { type: 'stream', pattern: 'tips-${appointmentId}' },
299
- },
300
- ],
301
- },
302
286
  specs: [
303
287
  {
304
288
  type: 'gherkin',
@@ -409,14 +393,6 @@ describe('react.specs.ts.ejs (react slice)', () => {
409
393
  name: 'notify milestone',
410
394
  server: {
411
395
  description: 'Notifies on milestone',
412
- data: {
413
- items: [
414
- {
415
- target: { type: 'Command', name: 'SendNotification' },
416
- destination: { type: 'stream', pattern: 'notif-${memberId}' },
417
- },
418
- ],
419
- },
420
396
  specs: [
421
397
  {
422
398
  type: 'gherkin',
@@ -532,14 +508,6 @@ describe('react.specs.ts.ejs (react slice)', () => {
532
508
  name: 'notify barber of cancellation',
533
509
  server: {
534
510
  description: 'Notifies barber when appointment is cancelled',
535
- data: {
536
- items: [
537
- {
538
- target: { type: 'Command', name: 'NotifyBarber' },
539
- destination: { type: 'stream', pattern: 'barber-${barberId}' },
540
- },
541
- ],
542
- },
543
511
  specs: [
544
512
  {
545
513
  type: 'gherkin',
@@ -15,14 +15,6 @@ describe('react.ts.ejs', () => {
15
15
  name: 'missing-when-slice',
16
16
  server: {
17
17
  description: 'A react slice with no When step',
18
- data: {
19
- items: [
20
- {
21
- target: { type: 'Command', name: 'DoSomething' },
22
- destination: { type: 'stream', pattern: 'things-${id}' },
23
- },
24
- ],
25
- },
26
18
  specs: [
27
19
  {
28
20
  type: 'gherkin',
@@ -113,14 +105,6 @@ describe('react.ts.ejs', () => {
113
105
  name: 'Send order confirmation',
114
106
  server: {
115
107
  description: 'Sends confirmation after order placed',
116
- data: {
117
- items: [
118
- {
119
- target: { type: 'Command', name: 'SendConfirmation' },
120
- destination: { type: 'stream', pattern: 'confirmations-${orderId}' },
121
- },
122
- ],
123
- },
124
108
  specs: [
125
109
  {
126
110
  type: 'gherkin',
@@ -238,14 +222,6 @@ describe('react.ts.ejs', () => {
238
222
  name: 'Notify Barber of Cancellation',
239
223
  server: {
240
224
  description: 'Notifies barber when appointment is cancelled',
241
- data: {
242
- items: [
243
- {
244
- target: { type: 'Command', name: 'NotifyBarber' },
245
- destination: { type: 'stream', pattern: 'barber-${barberId}' },
246
- },
247
- ],
248
- },
249
225
  specs: [
250
226
  {
251
227
  type: 'gherkin',
@@ -335,7 +311,6 @@ describe('react.ts.ejs', () => {
335
311
  items: [
336
312
  {
337
313
  target: { type: 'Event', name: 'BarberNotified' },
338
- destination: { type: 'stream', pattern: 'barber-${barberId}' },
339
314
  },
340
315
  ],
341
316
  },
@@ -466,14 +441,6 @@ export type BarberNotified = Event<
466
441
  name: 'notify barber of cancellation',
467
442
  server: {
468
443
  description: 'Notifies barber when appointment is cancelled',
469
- data: {
470
- items: [
471
- {
472
- target: { type: 'Command', name: 'NotifyBarber' },
473
- destination: { type: 'stream', pattern: 'barber-${barberId}' },
474
- },
475
- ],
476
- },
477
444
  specs: [
478
445
  {
479
446
  type: 'gherkin',
@@ -607,14 +574,6 @@ export type BarberNotified = Event<
607
574
  name: 'notify milestone',
608
575
  server: {
609
576
  description: 'Notifies on milestone',
610
- data: {
611
- items: [
612
- {
613
- target: { type: 'Command', name: 'SendNotification' },
614
- destination: { type: 'stream', pattern: 'notif-${memberId}' },
615
- },
616
- ],
617
- },
618
577
  specs: [
619
578
  {
620
579
  type: 'gherkin',
@@ -728,14 +687,6 @@ export type BarberNotified = Event<
728
687
  name: 'check record updates',
729
688
  server: {
730
689
  description: 'Updates records after workout',
731
- data: {
732
- items: [
733
- {
734
- target: { type: 'Command', name: 'UpdateRecord' },
735
- destination: { type: 'stream', pattern: 'records-${memberId}' },
736
- },
737
- ],
738
- },
739
690
  specs: [
740
691
  {
741
692
  type: 'gherkin',
@@ -5,9 +5,11 @@ import { join } from 'node:path';
5
5
  import type { CommandSlice, Model, Narrative, QuerySlice } from '@auto-engineer/narrative';
6
6
  import fs from 'fs-extra';
7
7
  import { afterEach, beforeEach, describe, expect, it } from 'vitest';
8
+ import { ScaffoldError, TemplateRenderError } from '../codegen/scaffoldFromSchema';
8
9
  import type { GenerateServerCommand, GenerateServerEvents, GenerationState } from './generate-server';
9
10
  import {
10
11
  cleanServerDir,
12
+ createServerFailureEvent,
11
13
  createSliceGeneratedEvent,
12
14
  deriveModelPath,
13
15
  emitSliceGeneratedForAffected,
@@ -472,3 +474,72 @@ describe('emitSliceGenerationFailedForDuplicates', () => {
472
474
  ]);
473
475
  });
474
476
  });
477
+
478
+ describe('createServerFailureEvent', () => {
479
+ it('extracts flow/slice/type and template from ScaffoldError wrapping TemplateRenderError', () => {
480
+ const rootCause = new TypeError('Cannot read properties of undefined');
481
+ const templateErr = new TemplateRenderError('decide.ts.ejs', rootCause);
482
+ const scaffoldErr = new ScaffoldError('Checkout', 'PlaceOrder', 'command', templateErr);
483
+
484
+ const event = createServerFailureEvent(makeCommand(), scaffoldErr);
485
+
486
+ expect(event).toEqual({
487
+ type: 'ServerGenerationFailed',
488
+ data: {
489
+ modelPath: path.join(path.resolve('.'), '.context', 'schema.json'),
490
+ destination: '.',
491
+ error: expect.stringContaining('[template=decide.ts.ejs]'),
492
+ model: makeModel(),
493
+ flowName: 'Checkout',
494
+ sliceName: 'PlaceOrder',
495
+ sliceType: 'command',
496
+ },
497
+ timestamp: expect.any(Date),
498
+ requestId: 'req-1',
499
+ correlationId: 'cor-1',
500
+ });
501
+ expect(event.data.error).toContain('Cannot read properties of undefined');
502
+ });
503
+
504
+ it('returns undefined flow/slice/type for plain Error', () => {
505
+ const plainError = new Error('something went wrong');
506
+
507
+ const event = createServerFailureEvent(makeCommand(), plainError);
508
+
509
+ expect(event).toEqual({
510
+ type: 'ServerGenerationFailed',
511
+ data: {
512
+ modelPath: path.join(path.resolve('.'), '.context', 'schema.json'),
513
+ destination: '.',
514
+ error: expect.stringContaining('something went wrong'),
515
+ model: makeModel(),
516
+ flowName: undefined,
517
+ sliceName: undefined,
518
+ sliceType: undefined,
519
+ },
520
+ timestamp: expect.any(Date),
521
+ requestId: 'req-1',
522
+ correlationId: 'cor-1',
523
+ });
524
+ });
525
+
526
+ it('returns Unknown error for non-Error value', () => {
527
+ const event = createServerFailureEvent(makeCommand(), 'string error');
528
+
529
+ expect(event).toEqual({
530
+ type: 'ServerGenerationFailed',
531
+ data: {
532
+ modelPath: path.join(path.resolve('.'), '.context', 'schema.json'),
533
+ destination: '.',
534
+ error: 'Unknown error occurred',
535
+ model: makeModel(),
536
+ flowName: undefined,
537
+ sliceName: undefined,
538
+ sliceType: undefined,
539
+ },
540
+ timestamp: expect.any(Date),
541
+ requestId: 'req-1',
542
+ correlationId: 'cor-1',
543
+ });
544
+ });
545
+ });
@@ -11,6 +11,8 @@ import {
11
11
  type DuplicateCommandInfo,
12
12
  type FieldIssue,
13
13
  generateScaffoldFilePlans,
14
+ ScaffoldError,
15
+ TemplateRenderError,
14
16
  writeScaffoldFilePlans,
15
17
  } from '../codegen/scaffoldFromSchema';
16
18
  import { ensureDirExists, ensureDirPath, toKebabCase } from '../codegen/utils/path';
@@ -80,6 +82,9 @@ export type ServerGenerationFailedEvent = Event<
80
82
  destination: string;
81
83
  error: string;
82
84
  model: Model;
85
+ flowName?: string;
86
+ sliceName?: string;
87
+ sliceType?: string;
83
88
  }
84
89
  >;
85
90
 
@@ -309,9 +314,36 @@ function stripAnsiCodes(text: string): string {
309
314
  return text.replace(/\x1B\[[0-9;]*m/g, '');
310
315
  }
311
316
 
312
- function createServerFailureEvent(command: GenerateServerCommand, error: unknown): ServerGenerationFailedEvent {
317
+ export function createServerFailureEvent(command: GenerateServerCommand, error: unknown): ServerGenerationFailedEvent {
313
318
  debug('Server generation failed with error: %O', error);
314
- const errorMessage = error instanceof Error ? stripAnsiCodes(error.stack ?? error.message) : 'Unknown error occurred';
319
+
320
+ let flowName: string | undefined;
321
+ let sliceName: string | undefined;
322
+ let sliceType: string | undefined;
323
+ let templateFile: string | undefined;
324
+ let errorMessage: string;
325
+
326
+ if (error instanceof Error) {
327
+ let current: unknown = error;
328
+ while (current instanceof Error) {
329
+ if (current instanceof ScaffoldError) {
330
+ flowName = current.flowName;
331
+ sliceName = current.sliceName;
332
+ sliceType = current.sliceType;
333
+ }
334
+ if (current instanceof TemplateRenderError) {
335
+ templateFile = current.templateFile;
336
+ }
337
+ current = current.cause;
338
+ }
339
+
340
+ const rootError = findRootCause(error);
341
+ const baseMessage = stripAnsiCodes(rootError.stack ?? rootError.message);
342
+ errorMessage = templateFile !== undefined ? `[template=${templateFile}] ${baseMessage}` : baseMessage;
343
+ } else {
344
+ errorMessage = 'Unknown error occurred';
345
+ }
346
+
315
347
  return {
316
348
  type: 'ServerGenerationFailed',
317
349
  data: {
@@ -319,6 +351,9 @@ function createServerFailureEvent(command: GenerateServerCommand, error: unknown
319
351
  destination: command.data.destination,
320
352
  error: errorMessage,
321
353
  model: command.data.model,
354
+ flowName,
355
+ sliceName,
356
+ sliceType,
322
357
  },
323
358
  timestamp: new Date(),
324
359
  requestId: command.requestId,
@@ -326,6 +361,14 @@ function createServerFailureEvent(command: GenerateServerCommand, error: unknown
326
361
  };
327
362
  }
328
363
 
364
+ function findRootCause(error: Error): Error {
365
+ let current: Error = error;
366
+ while (current.cause instanceof Error) {
367
+ current = current.cause;
368
+ }
369
+ return current;
370
+ }
371
+
329
372
  export function createSliceGeneratedEvent(
330
373
  flow: Narrative,
331
374
  slice: Slice,