@auto-engineer/server-generator-apollo-emmett 0.12.1 → 0.13.1

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 (92) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +20 -0
  3. package/dist/src/codegen/extract/commands.d.ts +7 -14
  4. package/dist/src/codegen/extract/commands.d.ts.map +1 -1
  5. package/dist/src/codegen/extract/commands.js.map +1 -1
  6. package/dist/src/codegen/extract/data-sink.d.ts +1 -1
  7. package/dist/src/codegen/extract/data-sink.d.ts.map +1 -1
  8. package/dist/src/codegen/extract/data-sink.js +9 -31
  9. package/dist/src/codegen/extract/data-sink.js.map +1 -1
  10. package/dist/src/codegen/extract/events.d.ts +4 -8
  11. package/dist/src/codegen/extract/events.d.ts.map +1 -1
  12. package/dist/src/codegen/extract/events.js.map +1 -1
  13. package/dist/src/codegen/extract/gwt.d.ts +2 -2
  14. package/dist/src/codegen/extract/gwt.d.ts.map +1 -1
  15. package/dist/src/codegen/extract/gwt.js +6 -21
  16. package/dist/src/codegen/extract/gwt.js.map +1 -1
  17. package/dist/src/codegen/extract/messages.d.ts +4 -4
  18. package/dist/src/codegen/extract/messages.d.ts.map +1 -1
  19. package/dist/src/codegen/extract/messages.js +24 -47
  20. package/dist/src/codegen/extract/messages.js.map +1 -1
  21. package/dist/src/codegen/extract/query.d.ts +5 -7
  22. package/dist/src/codegen/extract/query.d.ts.map +1 -1
  23. package/dist/src/codegen/extract/query.js +10 -8
  24. package/dist/src/codegen/extract/query.js.map +1 -1
  25. package/dist/src/codegen/extract/slice-normalizer.d.ts +29 -0
  26. package/dist/src/codegen/extract/slice-normalizer.d.ts.map +1 -0
  27. package/dist/src/codegen/extract/slice-normalizer.js +48 -0
  28. package/dist/src/codegen/extract/slice-normalizer.js.map +1 -0
  29. package/dist/src/codegen/extract/step-converter.d.ts +17 -0
  30. package/dist/src/codegen/extract/step-converter.d.ts.map +1 -0
  31. package/dist/src/codegen/extract/step-converter.js +82 -0
  32. package/dist/src/codegen/extract/step-converter.js.map +1 -0
  33. package/dist/src/codegen/extract/step-types.d.ts +30 -0
  34. package/dist/src/codegen/extract/step-types.d.ts.map +1 -0
  35. package/dist/src/codegen/extract/step-types.js +16 -0
  36. package/dist/src/codegen/extract/step-types.js.map +1 -0
  37. package/dist/src/codegen/scaffoldFromSchema.d.ts.map +1 -1
  38. package/dist/src/codegen/scaffoldFromSchema.js +7 -29
  39. package/dist/src/codegen/scaffoldFromSchema.js.map +1 -1
  40. package/dist/src/codegen/templates/command/commands.specs.ts +33 -28
  41. package/dist/src/codegen/templates/command/decide.specs.specs.ts +153 -138
  42. package/dist/src/codegen/templates/command/decide.specs.ts +169 -146
  43. package/dist/src/codegen/templates/command/events.specs.ts +43 -38
  44. package/dist/src/codegen/templates/command/evolve.specs.ts +38 -33
  45. package/dist/src/codegen/templates/command/handle.specs.ts +71 -61
  46. package/dist/src/codegen/templates/command/mutation.resolver.specs.ts +122 -102
  47. package/dist/src/codegen/templates/command/register.specs.ts +39 -34
  48. package/dist/src/codegen/templates/command/state.specs.ts +39 -34
  49. package/dist/src/codegen/templates/query/projection.specs.specs.ts +399 -359
  50. package/dist/src/codegen/templates/query/projection.specs.ts +242 -216
  51. package/dist/src/codegen/templates/query/projection.specs.ts.ejs +38 -12
  52. package/dist/src/codegen/templates/query/query.resolver.specs.ts +59 -51
  53. package/dist/src/codegen/templates/query/state.specs.ts +1 -1
  54. package/dist/src/codegen/templates/react/react.specs.specs.ts +93 -85
  55. package/dist/src/codegen/templates/react/react.specs.ts +135 -122
  56. package/dist/src/codegen/templates/react/register.specs.ts +135 -122
  57. package/dist/src/codegen/test-data/specVariant1.d.ts.map +1 -1
  58. package/dist/src/codegen/test-data/specVariant1.js +101 -90
  59. package/dist/src/codegen/test-data/specVariant1.js.map +1 -1
  60. package/dist/src/codegen/types.d.ts +5 -7
  61. package/dist/src/codegen/types.d.ts.map +1 -1
  62. package/dist/tsconfig.tsbuildinfo +1 -1
  63. package/package.json +4 -4
  64. package/src/codegen/extract/commands.ts +7 -8
  65. package/src/codegen/extract/data-sink.ts +16 -43
  66. package/src/codegen/extract/events.ts +4 -5
  67. package/src/codegen/extract/gwt.ts +12 -29
  68. package/src/codegen/extract/messages.ts +43 -70
  69. package/src/codegen/extract/query.ts +14 -12
  70. package/src/codegen/extract/slice-normalizer.ts +88 -0
  71. package/src/codegen/extract/step-converter.ts +124 -0
  72. package/src/codegen/extract/step-types.ts +52 -0
  73. package/src/codegen/scaffoldFromSchema.ts +8 -45
  74. package/src/codegen/templates/command/commands.specs.ts +33 -28
  75. package/src/codegen/templates/command/decide.specs.specs.ts +153 -138
  76. package/src/codegen/templates/command/decide.specs.ts +169 -146
  77. package/src/codegen/templates/command/events.specs.ts +43 -38
  78. package/src/codegen/templates/command/evolve.specs.ts +38 -33
  79. package/src/codegen/templates/command/handle.specs.ts +71 -61
  80. package/src/codegen/templates/command/mutation.resolver.specs.ts +122 -102
  81. package/src/codegen/templates/command/register.specs.ts +39 -34
  82. package/src/codegen/templates/command/state.specs.ts +39 -34
  83. package/src/codegen/templates/query/projection.specs.specs.ts +399 -359
  84. package/src/codegen/templates/query/projection.specs.ts +242 -216
  85. package/src/codegen/templates/query/projection.specs.ts.ejs +38 -12
  86. package/src/codegen/templates/query/query.resolver.specs.ts +59 -51
  87. package/src/codegen/templates/query/state.specs.ts +1 -1
  88. package/src/codegen/templates/react/react.specs.specs.ts +93 -85
  89. package/src/codegen/templates/react/react.specs.ts +135 -122
  90. package/src/codegen/templates/react/register.specs.ts +135 -122
  91. package/src/codegen/test-data/specVariant1.ts +101 -90
  92. package/src/codegen/types.ts +6 -4
@@ -0,0 +1,52 @@
1
+ import type { Step, Slice } from '@auto-engineer/narrative';
2
+ import type { StepWithDocStringSchema, StepWithErrorSchema } from '@auto-engineer/narrative';
3
+ import type { z } from 'zod';
4
+
5
+ export type StepWithDocString = z.infer<typeof StepWithDocStringSchema>;
6
+ export type StepWithError = z.infer<typeof StepWithErrorSchema>;
7
+
8
+ export type StepKeyword = StepWithDocString['keyword'];
9
+ export type MajorKeyword = Exclude<StepKeyword, 'And'>;
10
+
11
+ export type ErrorType = StepWithError['error']['type'];
12
+
13
+ export type SliceType = Extract<Slice['type'], 'command' | 'query' | 'react'>;
14
+
15
+ export function getSliceType(slice: Slice): SliceType {
16
+ if (slice.type === 'command' || slice.type === 'query' || slice.type === 'react') {
17
+ return slice.type;
18
+ }
19
+ return 'command';
20
+ }
21
+
22
+ export function isStepWithDocString(step: Step): step is StepWithDocString {
23
+ return 'text' in step;
24
+ }
25
+
26
+ export function isStepWithError(step: Step): step is StepWithError {
27
+ return 'error' in step;
28
+ }
29
+
30
+ export function resolveMajorKeyword(keyword: StepKeyword, current: MajorKeyword): MajorKeyword {
31
+ return keyword === 'And' ? current : keyword;
32
+ }
33
+
34
+ export interface CommandRef {
35
+ commandRef: string;
36
+ exampleData: Record<string, unknown>;
37
+ }
38
+
39
+ export interface EventRef {
40
+ eventRef: string;
41
+ exampleData: Record<string, unknown>;
42
+ }
43
+
44
+ export interface StateRef {
45
+ stateRef: string;
46
+ exampleData: Record<string, unknown>;
47
+ }
48
+
49
+ export interface ErrorRef {
50
+ errorType: ErrorType;
51
+ message?: string;
52
+ }
@@ -34,21 +34,8 @@ import {
34
34
  createFieldUsesFloat,
35
35
  createCollectEnumNames,
36
36
  } from './extract';
37
-
38
- function extractGwtSpecs(slice: Slice) {
39
- if (!('server' in slice)) return [];
40
- const specs = slice.server?.specs;
41
- const rules = specs?.rules;
42
- return Array.isArray(rules) && rules.length > 0
43
- ? rules.flatMap((rule) =>
44
- rule.examples.map((example) => ({
45
- given: example.given,
46
- when: example.when,
47
- then: example.then,
48
- })),
49
- )
50
- : [];
51
- }
37
+ import { extractGwtSpecsFromSlice, type GwtResult } from './extract/step-converter';
38
+ import { normalizeSliceForTemplate } from './extract/slice-normalizer';
52
39
  import { Message, MessageDefinition, GwtCondition } from './types';
53
40
  import { parseGraphQlRequest } from './extract/graphql';
54
41
  import { getStreamFromSink } from './extract/data-sink';
@@ -529,7 +516,7 @@ async function prepareTemplateData(
529
516
  return {
530
517
  flowName: flow.name,
531
518
  sliceName: slice.name,
532
- slice,
519
+ slice: normalizeSliceForTemplate(slice),
533
520
  stream: { pattern: streamPattern, id: streamId },
534
521
  commands: filteredCommands,
535
522
  events,
@@ -571,13 +558,8 @@ function annotateEventSources(
571
558
  }
572
559
  }
573
560
 
574
- function hasEventInGwtSpecs(gwtSpecs: ReturnType<typeof extractGwtSpecs>, eventType: string): boolean {
575
- return gwtSpecs.some((g) =>
576
- g.then.some(
577
- (t) =>
578
- typeof t === 'object' && t !== null && 'eventRef' in t && (t as { eventRef: string }).eventRef === eventType,
579
- ),
580
- );
561
+ function hasEventInGwtSpecs(gwtSpecs: GwtResult[], eventType: string): boolean {
562
+ return gwtSpecs.some((g) => g.then.some((t) => 'eventRef' in t && t.eventRef === eventType));
581
563
  }
582
564
 
583
565
  function canSliceProduceEvent(slice: Slice): boolean {
@@ -591,7 +573,7 @@ function findEventSource(flows: Narrative[], eventType: string): { flowName: str
591
573
  for (const slice of flow.slices) {
592
574
  if (!canSliceProduceEvent(slice)) continue;
593
575
 
594
- const gwtSpecs = extractGwtSpecs(slice);
576
+ const gwtSpecs = extractGwtSpecsFromSlice(slice);
595
577
  if (hasEventInGwtSpecs(gwtSpecs, eventType)) {
596
578
  debugSlice(' Found event source in flow: %s, slice: %s', flow.name, slice.name);
597
579
  return { flowName: flow.name, sliceName: slice.name };
@@ -624,27 +606,8 @@ function findCommandSource(flows: Narrative[], commandType: string): { flowName:
624
606
  for (const slice of flow.slices) {
625
607
  if (slice.type !== 'command') continue;
626
608
 
627
- const specs = slice.server?.specs;
628
- const rules = specs?.rules;
629
- const gwtSpecs =
630
- Array.isArray(rules) && rules.length > 0
631
- ? rules.flatMap((rule) =>
632
- rule.examples.map((example) => ({
633
- given: example.given,
634
- when: example.when,
635
- then: example.then,
636
- })),
637
- )
638
- : [];
639
- if (
640
- gwtSpecs.some(
641
- (g) =>
642
- g.when !== undefined &&
643
- !Array.isArray(g.when) &&
644
- 'commandRef' in g.when &&
645
- g.when.commandRef === commandType,
646
- )
647
- ) {
609
+ const gwtSpecs = extractGwtSpecsFromSlice(slice);
610
+ if (gwtSpecs.some((g) => !Array.isArray(g.when) && 'commandRef' in g.when && g.when.commandRef === commandType)) {
648
611
  debugSlice(' Found command source in flow: %s, slice: %s', flow.name, slice.name);
649
612
  return { flowName: flow.name, sliceName: slice.name };
650
613
  }
@@ -18,35 +18,40 @@ describe('commands.ts.ejs', () => {
18
18
  },
19
19
  server: {
20
20
  description: 'test',
21
- specs: {
22
- name: 'Create listing command',
23
- rules: [
24
- {
25
- description: 'Should accept valid listing data',
26
- examples: [
27
- {
28
- description: 'User creates listing with valid data',
29
- when: {
30
- commandRef: 'CreateListing',
31
- exampleData: {
32
- propertyId: 'listing_123',
33
- title: 'nice apartment',
34
- pricePerNight: 250,
35
- maxGuests: 4,
36
- amenities: ['wifi', 'kitchen', 'parking'],
37
- available: true,
38
- tags: ['sea view', 'balcony'],
39
- rating: 4.8,
40
- metadata: { petsAllowed: true },
41
- listedAt: '2024-01-15T10:00:00Z',
42
- },
21
+ specs: [
22
+ {
23
+ type: 'gherkin',
24
+ feature: 'Create listing command',
25
+ rules: [
26
+ {
27
+ name: 'Should accept valid listing data',
28
+ examples: [
29
+ {
30
+ name: 'User creates listing with valid data',
31
+ steps: [
32
+ {
33
+ keyword: 'When',
34
+ text: 'CreateListing',
35
+ docString: {
36
+ propertyId: 'listing_123',
37
+ title: 'nice apartment',
38
+ pricePerNight: 250,
39
+ maxGuests: 4,
40
+ amenities: ['wifi', 'kitchen', 'parking'],
41
+ available: true,
42
+ tags: ['sea view', 'balcony'],
43
+ rating: 4.8,
44
+ metadata: { petsAllowed: true },
45
+ listedAt: '2024-01-15T10:00:00Z',
46
+ },
47
+ },
48
+ ],
43
49
  },
44
- then: [],
45
- },
46
- ],
47
- },
48
- ],
49
- },
50
+ ],
51
+ },
52
+ ],
53
+ },
54
+ ],
50
55
  },
51
56
  },
52
57
  ],
@@ -16,45 +16,50 @@ describe('spec.ts.ejs', () => {
16
16
  client: { specs: [] },
17
17
  server: {
18
18
  description: '',
19
- specs: {
20
- name: 'Create listing spec',
21
- rules: [
22
- {
23
- description: 'Should create listing successfully',
24
- examples: [
25
- {
26
- description: 'User creates listing with valid data',
27
- when: {
28
- commandRef: 'CreateListing',
29
- exampleData: {
30
- propertyId: 'listing_123',
31
- title: 'blah',
32
- pricePerNight: 250,
33
- maxGuests: 4,
34
- amenities: ['wifi', 'kitchen'],
35
- available: true,
36
- tags: ['some tag'],
37
- rating: 4.8,
38
- metadata: { foo: 'bar' },
39
- listedAt: '2024-01-15T10:00:00Z',
40
- },
41
- },
42
- then: [
43
- {
44
- eventRef: 'ListingCreated',
45
- exampleData: {
46
- propertyId: 'listing_123',
47
- listedAt: '2024-01-15T10:00:00Z',
48
- rating: 4.8,
49
- metadata: { foo: 'bar' },
19
+ specs: [
20
+ {
21
+ type: 'gherkin',
22
+ feature: 'Create listing spec',
23
+ rules: [
24
+ {
25
+ name: 'Should create listing successfully',
26
+ examples: [
27
+ {
28
+ name: 'User creates listing with valid data',
29
+ steps: [
30
+ {
31
+ keyword: 'When',
32
+ text: 'CreateListing',
33
+ docString: {
34
+ propertyId: 'listing_123',
35
+ title: 'blah',
36
+ pricePerNight: 250,
37
+ maxGuests: 4,
38
+ amenities: ['wifi', 'kitchen'],
39
+ available: true,
40
+ tags: ['some tag'],
41
+ rating: 4.8,
42
+ metadata: { foo: 'bar' },
43
+ listedAt: '2024-01-15T10:00:00Z',
44
+ },
50
45
  },
51
- },
52
- ],
53
- },
54
- ],
55
- },
56
- ],
57
- },
46
+ {
47
+ keyword: 'Then',
48
+ text: 'ListingCreated',
49
+ docString: {
50
+ propertyId: 'listing_123',
51
+ listedAt: '2024-01-15T10:00:00Z',
52
+ rating: 4.8,
53
+ metadata: { foo: 'bar' },
54
+ },
55
+ },
56
+ ],
57
+ },
58
+ ],
59
+ },
60
+ ],
61
+ },
62
+ ],
58
63
  },
59
64
  },
60
65
  ],
@@ -160,45 +165,49 @@ describe('spec.ts.ejs', () => {
160
165
  client: { specs: [] },
161
166
  server: {
162
167
  description: '',
163
- specs: {
164
- name: 'Remove listing spec',
165
- rules: [
166
- {
167
- description: 'Should remove existing listing',
168
- examples: [
169
- {
170
- description: 'Existing listing can be removed',
171
- given: [
172
- {
173
- eventRef: 'ListingCreated',
174
- exampleData: {
175
- propertyId: 'listing_123',
176
- listedAt: '2024-01-15T10:00:00Z',
177
- rating: 4.8,
178
- metadata: { foo: 'bar' },
168
+ specs: [
169
+ {
170
+ type: 'gherkin',
171
+ feature: 'Remove listing spec',
172
+ rules: [
173
+ {
174
+ name: 'Should remove existing listing',
175
+ examples: [
176
+ {
177
+ name: 'Existing listing can be removed',
178
+ steps: [
179
+ {
180
+ keyword: 'Given',
181
+ text: 'ListingCreated',
182
+ docString: {
183
+ propertyId: 'listing_123',
184
+ listedAt: '2024-01-15T10:00:00Z',
185
+ rating: 4.8,
186
+ metadata: { foo: 'bar' },
187
+ },
179
188
  },
180
- },
181
- ],
182
- when: {
183
- commandRef: 'RemoveListing',
184
- exampleData: {
185
- propertyId: 'listing_123',
186
- },
187
- },
188
- then: [
189
- {
190
- eventRef: 'ListingRemoved',
191
- exampleData: {
192
- propertyId: 'listing_123',
193
- removedAt: '2024-01-16T10:00:00Z',
189
+ {
190
+ keyword: 'When',
191
+ text: 'RemoveListing',
192
+ docString: {
193
+ propertyId: 'listing_123',
194
+ },
194
195
  },
195
- },
196
- ],
197
- },
198
- ],
199
- },
200
- ],
201
- },
196
+ {
197
+ keyword: 'Then',
198
+ text: 'ListingRemoved',
199
+ docString: {
200
+ propertyId: 'listing_123',
201
+ removedAt: '2024-01-16T10:00:00Z',
202
+ },
203
+ },
204
+ ],
205
+ },
206
+ ],
207
+ },
208
+ ],
209
+ },
210
+ ],
202
211
  },
203
212
  },
204
213
  ],
@@ -302,73 +311,79 @@ describe('spec.ts.ejs', () => {
302
311
  client: { specs: [] },
303
312
  server: {
304
313
  description: '',
305
- specs: {
306
- name: 'Answer question spec',
307
- rules: [
308
- {
309
- description: 'answers are allowed while the questionnaire has not been submitted',
310
- examples: [
311
- {
312
- description: 'no questions have been answered yet',
313
- when: {
314
- commandRef: 'AnswerQuestion',
315
- exampleData: {
316
- questionnaireId: 'q-001',
317
- participantId: 'participant-abc',
318
- questionId: 'q1',
319
- answer: 'Yes',
320
- },
321
- },
322
- then: [
323
- {
324
- eventRef: 'QuestionAnswered',
325
- exampleData: {
326
- questionnaireId: 'q-001',
327
- participantId: 'participant-abc',
328
- questionId: 'q1',
329
- answer: 'Yes',
330
- savedAt: '2030-01-01T09:05:00.000Z',
314
+ specs: [
315
+ {
316
+ type: 'gherkin',
317
+ feature: 'Answer question spec',
318
+ rules: [
319
+ {
320
+ name: 'answers are allowed while the questionnaire has not been submitted',
321
+ examples: [
322
+ {
323
+ name: 'no questions have been answered yet',
324
+ steps: [
325
+ {
326
+ keyword: 'When',
327
+ text: 'AnswerQuestion',
328
+ docString: {
329
+ questionnaireId: 'q-001',
330
+ participantId: 'participant-abc',
331
+ questionId: 'q1',
332
+ answer: 'Yes',
333
+ },
331
334
  },
332
- },
333
- ],
334
- },
335
- {
336
- description: 'all questions have already been answered and submitted',
337
- given: [
338
- {
339
- eventRef: 'QuestionnaireSubmitted',
340
- exampleData: {
341
- questionnaireId: 'q-001',
342
- participantId: 'participant-abc',
343
- submittedAt: '2030-01-01T09:00:00.000Z',
335
+ {
336
+ keyword: 'Then',
337
+ text: 'QuestionAnswered',
338
+ docString: {
339
+ questionnaireId: 'q-001',
340
+ participantId: 'participant-abc',
341
+ questionId: 'q1',
342
+ answer: 'Yes',
343
+ savedAt: '2030-01-01T09:05:00.000Z',
344
+ },
344
345
  },
345
- },
346
- ],
347
- when: {
348
- commandRef: 'AnswerQuestion',
349
- exampleData: {
350
- questionnaireId: 'q-001',
351
- participantId: 'participant-abc',
352
- questionId: 'q1',
353
- answer: 'Yes',
354
- },
346
+ ],
355
347
  },
356
- then: [
357
- {
358
- eventRef: 'QuestionnaireEditRejected',
359
- exampleData: {
360
- questionnaireId: 'q-001',
361
- participantId: 'participant-abc',
362
- reason: 'Questionnaire already submitted',
363
- attemptedAt: '2030-01-01T09:05:00.000Z',
348
+ {
349
+ name: 'all questions have already been answered and submitted',
350
+ steps: [
351
+ {
352
+ keyword: 'Given',
353
+ text: 'QuestionnaireSubmitted',
354
+ docString: {
355
+ questionnaireId: 'q-001',
356
+ participantId: 'participant-abc',
357
+ submittedAt: '2030-01-01T09:00:00.000Z',
358
+ },
364
359
  },
365
- },
366
- ],
367
- },
368
- ],
369
- },
370
- ],
371
- },
360
+ {
361
+ keyword: 'When',
362
+ text: 'AnswerQuestion',
363
+ docString: {
364
+ questionnaireId: 'q-001',
365
+ participantId: 'participant-abc',
366
+ questionId: 'q1',
367
+ answer: 'Yes',
368
+ },
369
+ },
370
+ {
371
+ keyword: 'Then',
372
+ text: 'QuestionnaireEditRejected',
373
+ docString: {
374
+ questionnaireId: 'q-001',
375
+ participantId: 'participant-abc',
376
+ reason: 'Questionnaire already submitted',
377
+ attemptedAt: '2030-01-01T09:05:00.000Z',
378
+ },
379
+ },
380
+ ],
381
+ },
382
+ ],
383
+ },
384
+ ],
385
+ },
386
+ ],
372
387
  },
373
388
  },
374
389
  ],