@auto-engineer/server-generator-apollo-emmett 1.83.0 → 1.85.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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @auto-engineer/server-generator-apollo-emmett@1.83.0 build /home/runner/work/auto-engineer/auto-engineer/packages/server-generator-apollo-emmett
2
+ > @auto-engineer/server-generator-apollo-emmett@1.85.0 build /home/runner/work/auto-engineer/auto-engineer/packages/server-generator-apollo-emmett
3
3
  > 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
4
4
 
5
5
  Fixed ESM imports in dist/
@@ -1,14 +1,14 @@
1
1
 
2
- > @auto-engineer/server-generator-apollo-emmett@1.82.0 test /home/runner/work/auto-engineer/auto-engineer/packages/server-generator-apollo-emmett
2
+ > @auto-engineer/server-generator-apollo-emmett@1.84.0 test /home/runner/work/auto-engineer/auto-engineer/packages/server-generator-apollo-emmett
3
3
  > vitest run --reporter=dot
4
4
 
5
5
 
6
6
   RUN  v3.2.4 /home/runner/work/auto-engineer/auto-engineer/packages/server-generator-apollo-emmett
7
7
 
8
- ······························································································································································-·················
8
+ ·····································································································································································-·················
9
9
 
10
-  Test Files  30 passed | 1 skipped (31)
11
-  Tests  175 passed | 1 skipped (176)
12
-  Start at  13:38:08
13
-  Duration  38.26s (transform 5.51s, setup 0ms, collect 75.49s, tests 21.59s, environment 17ms, prepare 6.31s)
10
+  Test Files  31 passed | 1 skipped (32)
11
+  Tests  182 passed | 1 skipped (183)
12
+  Start at  01:48:10
13
+  Duration  30.35s (transform 6.59s, setup 0ms, collect 65.14s, tests 10.75s, environment 8ms, prepare 5.64s)
14
14
 
@@ -1,4 +1,4 @@
1
1
 
2
- > @auto-engineer/server-generator-apollo-emmett@1.82.0 type-check /home/runner/work/auto-engineer/auto-engineer/packages/server-generator-apollo-emmett
2
+ > @auto-engineer/server-generator-apollo-emmett@1.84.0 type-check /home/runner/work/auto-engineer/auto-engineer/packages/server-generator-apollo-emmett
3
3
  > tsc --noEmit --project tsconfig.json
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # @auto-engineer/server-generator-apollo-emmett
2
2
 
3
+ ## 1.85.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`eef47a0`](https://github.com/BeOnAuto/auto-engineer/commit/eef47a0424e6528ea832a907dacc8bc398b34dfe) Thanks [@github-actions[bot]](https://github.com/github-actions%5Bbot%5D)! - - **server-generator-apollo-emmett**: merge co-firing GWT conditions in gwt.ts
8
+ - **server-generator-apollo-emmett**: fix singleton projection instruction steps 1 and 4
9
+ - **global**: version packages
10
+ - **server-generator-apollo-emmett**: add snapshot test for co-firing rule merge
11
+ - **server-implementer**: add Bursts 4-5 to ketchup plan
12
+
13
+ - [`f90636d`](https://github.com/BeOnAuto/auto-engineer/commit/f90636d198db18032ce9b9e8f165d158ad9d0176) Thanks [@SamHatoum](https://github.com/SamHatoum)! - - **generate-react-client**: build-component-db uploads to artifact path via env vars
14
+
15
+ ### Patch Changes
16
+
17
+ - Updated dependencies [[`eef47a0`](https://github.com/BeOnAuto/auto-engineer/commit/eef47a0424e6528ea832a907dacc8bc398b34dfe), [`f90636d`](https://github.com/BeOnAuto/auto-engineer/commit/f90636d198db18032ce9b9e8f165d158ad9d0176)]:
18
+ - @auto-engineer/message-bus@1.85.0
19
+ - @auto-engineer/narrative@1.85.0
20
+
21
+ ## 1.84.0
22
+
23
+ ### Minor Changes
24
+
25
+ - [`7b5e912`](https://github.com/BeOnAuto/auto-engineer/commit/7b5e912dfda709fd3d33bf478f515d4e86c982af) Thanks [@github-actions[bot]](https://github.com/github-actions%5Bbot%5D)! - - **server-implementer**: integrate shadow detection into retry loops
26
+ - **server-implementer**: add detectImportedTypeShadowing utility
27
+ - **server-generator-nestjs**: add model to ServerGenerationFailedEvent
28
+ - **server-generator-apollo-emmett**: add model to failure events
29
+ - **pipeline**: emit PipelineRunCompleted when all commands complete
30
+
31
+ - [`ba4b32f`](https://github.com/BeOnAuto/auto-engineer/commit/ba4b32f29fbfb45e14a6294fdc06583e466fb7e3) Thanks [@rami-hatoum](https://github.com/rami-hatoum)! - - **server-generator-apollo-emmett**: merge co-firing GWT conditions in gwt.ts
32
+
33
+ ### Patch Changes
34
+
35
+ - [`3271cfe`](https://github.com/BeOnAuto/auto-engineer/commit/3271cfe4b8be6316454045b02e1e0f1d1fd6414f) Thanks [@rami-hatoum](https://github.com/rami-hatoum)! - - **server-generator-apollo-emmett**: fix singleton projection instruction steps 1 and 4
36
+
37
+ - [`4833113`](https://github.com/BeOnAuto/auto-engineer/commit/4833113ca52361ca4a4c931390a48b592f1adc78) Thanks [@rami-hatoum](https://github.com/rami-hatoum)! - - **server-generator-apollo-emmett**: add snapshot test for co-firing rule merge
38
+
39
+ - [`c3c788d`](https://github.com/BeOnAuto/auto-engineer/commit/c3c788defe899dd9c3d50819759c15df764fab2e) Thanks [@rami-hatoum](https://github.com/rami-hatoum)! - - **server-implementer**: add Bursts 4-5 to ketchup plan
40
+
41
+ - Updated dependencies [[`7b5e912`](https://github.com/BeOnAuto/auto-engineer/commit/7b5e912dfda709fd3d33bf478f515d4e86c982af), [`3271cfe`](https://github.com/BeOnAuto/auto-engineer/commit/3271cfe4b8be6316454045b02e1e0f1d1fd6414f), [`ba4b32f`](https://github.com/BeOnAuto/auto-engineer/commit/ba4b32f29fbfb45e14a6294fdc06583e466fb7e3), [`4833113`](https://github.com/BeOnAuto/auto-engineer/commit/4833113ca52361ca4a4c931390a48b592f1adc78), [`c3c788d`](https://github.com/BeOnAuto/auto-engineer/commit/c3c788defe899dd9c3d50819759c15df764fab2e)]:
42
+ - @auto-engineer/message-bus@1.84.0
43
+ - @auto-engineer/narrative@1.84.0
44
+
3
45
  ## 1.83.0
4
46
 
5
47
  ### Minor Changes
@@ -3,4 +3,5 @@ import type { GwtCondition } from '../types';
3
3
  export declare function buildCommandGwtMapping(slice: Slice): Record<string, (GwtCondition & {
4
4
  failingFields?: string[];
5
5
  })[]>;
6
+ export declare function mergeCoFiringConditions(conditions: GwtCondition[]): GwtCondition[];
6
7
  //# sourceMappingURL=gwt.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gwt.d.ts","sourceRoot":"","sources":["../../../../src/codegen/extract/gwt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,KAAK,EAAwB,YAAY,EAAE,MAAM,UAAU,CAAC;AAGnE,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,YAAY,GAAG;IAAE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,EAAE,CAAC,CAQpH"}
1
+ {"version":3,"file":"gwt.d.ts","sourceRoot":"","sources":["../../../../src/codegen/extract/gwt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,KAAK,EAAwB,YAAY,EAAE,MAAM,UAAU,CAAC;AAGnE,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,YAAY,GAAG;IAAE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,EAAE,CAAC,CAQpH;AAiCD,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CA4BlF"}
@@ -28,8 +28,42 @@ function buildCommandMapping(gwtSpecs) {
28
28
  });
29
29
  }
30
30
  }
31
+ for (const command in mapping) {
32
+ mapping[command] = mergeCoFiringConditions(mapping[command]);
33
+ }
31
34
  return mapping;
32
35
  }
36
+ export function mergeCoFiringConditions(conditions) {
37
+ const merged = [];
38
+ const keyToIndex = new Map();
39
+ for (const condition of conditions) {
40
+ const isErrorCase = condition.then.some((t) => 'errorType' in t);
41
+ if (isErrorCase) {
42
+ merged.push(condition);
43
+ continue;
44
+ }
45
+ const key = stableKey(condition);
46
+ const existingIdx = keyToIndex.get(key);
47
+ if (existingIdx !== undefined) {
48
+ const existing = merged[existingIdx];
49
+ for (const t of condition.then) {
50
+ if ('eventRef' in t && !existing.then.some((e) => 'eventRef' in e && e.eventRef === t.eventRef)) {
51
+ existing.then.push(t);
52
+ }
53
+ }
54
+ }
55
+ else {
56
+ keyToIndex.set(key, merged.length);
57
+ merged.push({ ...condition, then: [...condition.then] });
58
+ }
59
+ }
60
+ return merged;
61
+ }
62
+ function stableKey(gwt) {
63
+ return JSON.stringify({ given: gwt.given, when: gwt.when }, (_, v) => v && typeof v === 'object' && !Array.isArray(v)
64
+ ? Object.fromEntries(Object.entries(v).sort(([a], [b]) => a.localeCompare(b)))
65
+ : v);
66
+ }
33
67
  function enhanceMapping(mapping) {
34
68
  const enhancedMapping = {};
35
69
  for (const command in mapping) {
@@ -1 +1 @@
1
- {"version":3,"file":"gwt.js","sourceRoot":"","sources":["../../../../src/codegen/extract/gwt.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gwt.js","sourceRoot":"","sources":["../../../../src/codegen/extract/gwt.ts"],"names":[],"mappings":"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"}
@@ -604,6 +604,156 @@ describe('spec.ts.ejs', () => {
604
604
  expect(contents).not.toContain('new Date(null)');
605
605
  });
606
606
 
607
+ it('should merge co-firing rules with same given+when into one test with combined events', async () => {
608
+ const spec: SpecsSchema = {
609
+ variant: 'specs',
610
+ narratives: [
611
+ {
612
+ name: 'Fitness flow',
613
+ slices: [
614
+ {
615
+ type: 'command',
616
+ name: 'Submit workout',
617
+ client: { specs: [] },
618
+ server: {
619
+ description: '',
620
+ specs: [
621
+ {
622
+ type: 'gherkin',
623
+ feature: 'Submit workout spec',
624
+ rules: [
625
+ {
626
+ name: 'Track exercise stats',
627
+ examples: [
628
+ {
629
+ name: 'Workout submitted updates stats',
630
+ steps: [
631
+ {
632
+ keyword: 'When',
633
+ text: 'SubmitWorkout',
634
+ docString: { workoutId: 'w1', exercise: 'squat' },
635
+ },
636
+ {
637
+ keyword: 'Then',
638
+ text: 'StatsUpdated',
639
+ docString: { workoutId: 'w1', count: 1 },
640
+ },
641
+ ],
642
+ },
643
+ ],
644
+ },
645
+ {
646
+ name: 'Track workout log',
647
+ examples: [
648
+ {
649
+ name: 'Workout submitted creates log',
650
+ steps: [
651
+ {
652
+ keyword: 'When',
653
+ text: 'SubmitWorkout',
654
+ docString: { workoutId: 'w1', exercise: 'squat' },
655
+ },
656
+ {
657
+ keyword: 'Then',
658
+ text: 'WorkoutLogged',
659
+ docString: { workoutId: 'w1', loggedAt: '2024-01-01T00:00:00Z' },
660
+ },
661
+ ],
662
+ },
663
+ ],
664
+ },
665
+ ],
666
+ },
667
+ ],
668
+ },
669
+ },
670
+ ],
671
+ },
672
+ ],
673
+ messages: [
674
+ {
675
+ type: 'command',
676
+ name: 'SubmitWorkout',
677
+ fields: [
678
+ { name: 'workoutId', type: 'string', required: true },
679
+ { name: 'exercise', type: 'string', required: true },
680
+ ],
681
+ },
682
+ {
683
+ type: 'event',
684
+ name: 'StatsUpdated',
685
+ source: 'internal',
686
+ fields: [
687
+ { name: 'workoutId', type: 'string', required: true },
688
+ { name: 'count', type: 'number', required: true },
689
+ ],
690
+ },
691
+ {
692
+ type: 'event',
693
+ name: 'WorkoutLogged',
694
+ source: 'internal',
695
+ fields: [
696
+ { name: 'workoutId', type: 'string', required: true },
697
+ { name: 'loggedAt', type: 'Date', required: true },
698
+ ],
699
+ },
700
+ ],
701
+ };
702
+
703
+ const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
704
+ const specFile = plans.find((p) => p.outputPath.endsWith('specs.ts'));
705
+
706
+ expect(specFile?.contents).toMatchInlineSnapshot(`
707
+ "import { describe, it } from 'vitest';
708
+ import { DeciderSpecification } from '@event-driven-io/emmett';
709
+ import { decide } from './decide';
710
+ import { evolve } from './evolve';
711
+ import { initialState, State } from './state';
712
+ import type { StatsUpdated, WorkoutLogged } from './events';
713
+ import type { SubmitWorkout } from './commands';
714
+
715
+ describe('Track exercise stats', () => {
716
+ type Events = StatsUpdated | WorkoutLogged;
717
+
718
+ const given = DeciderSpecification.for<SubmitWorkout, Events, State>({
719
+ decide,
720
+ evolve,
721
+ initialState,
722
+ });
723
+
724
+ it('Workout submitted updates stats', () => {
725
+ given([])
726
+ .when({
727
+ type: 'SubmitWorkout',
728
+ data: {
729
+ workoutId: 'w1',
730
+ exercise: 'squat',
731
+ },
732
+ metadata: { now: new Date() },
733
+ })
734
+
735
+ .then([
736
+ {
737
+ type: 'StatsUpdated',
738
+ data: {
739
+ workoutId: 'w1',
740
+ count: 1,
741
+ },
742
+ },
743
+ {
744
+ type: 'WorkoutLogged',
745
+ data: {
746
+ workoutId: 'w1',
747
+ loggedAt: new Date('2024-01-01T00:00:00Z'),
748
+ },
749
+ },
750
+ ]);
751
+ });
752
+ });
753
+ "
754
+ `);
755
+ });
756
+
607
757
  it('should include business rule descriptions in decide.ts implementation comments', async () => {
608
758
  const spec: SpecsSchema = {
609
759
  variant: 'specs',
@@ -574,26 +574,20 @@ describe('projection.ts.ejs', () => {
574
574
  *
575
575
  * CRITICAL: Use internal state to track individual entity information:
576
576
  *
577
- * 1. Access current state:
578
- * const current: InternalTodoSummary = document ?? { ...initialState, _entities: {} };
577
+ * 1. Access current state (guard _entities):
578
+ * const current: InternalTodoSummary = document ?? { ...initialState };
579
+ * if (!current._entities) current._entities = {};
579
580
  *
580
581
  * 2. Track entity changes:
581
- * // a) Extract the unique identifier that distinguishes this entity
582
- * // Examine event.data to find the ID field (often 'id' or '<entity>Id')
583
582
  * const entityId = event.data.[ENTITY_ID_FIELD];
584
- *
585
- * // b) Store/update entity state with relevant properties from event.data
586
- * // Include only fields needed for aggregation calculations
587
583
  * current._entities[entityId] = { [field]: value, ... };
588
584
  *
589
585
  * 3. Calculate aggregates from entity states:
590
- * const counts = Object.values(current._entities).reduce((acc, entity) => {
591
- * acc[entity.status] = (acc[entity.status] || 0) + 1;
592
- * return acc;
593
- * }, {});
586
+ * const aggregated = Object.values(current._entities).reduce(...);
594
587
  *
595
- * 4. Return with internal state:
596
- * return { ...publicFields, _entities: current._entities } as InternalTodoSummary;
588
+ * 4. Return via typed variable (preserves _entities for next event):
589
+ * const result: InternalTodoSummary = { ...publicFields, _entities: current._entities };
590
+ * return result;
597
591
 
598
592
  * Event (TodoAdded) fields: todoId: string, title: string
599
593
  */
@@ -127,26 +127,20 @@ case '<%= event.type %>': {
127
127
  *
128
128
  * CRITICAL: Use internal state to track individual entity information:
129
129
  *
130
- * 1. Access current state:
131
- * const current: Internal<%= pascalCase(targetName || 'State') %> = document ?? { ...initialState, _entities: {} };
130
+ * 1. Access current state (guard _entities):
131
+ * const current: Internal<%= pascalCase(targetName || 'State') %> = document ?? { ...initialState };
132
+ * if (!current._entities) current._entities = {};
132
133
  *
133
134
  * 2. Track entity changes:
134
- * // a) Extract the unique identifier that distinguishes this entity
135
- * // Examine event.data to find the ID field (often 'id' or '<entity>Id')
136
135
  * const entityId = event.data.[ENTITY_ID_FIELD];
137
- *
138
- * // b) Store/update entity state with relevant properties from event.data
139
- * // Include only fields needed for aggregation calculations
140
136
  * current._entities[entityId] = { [field]: value, ... };
141
137
  *
142
138
  * 3. Calculate aggregates from entity states:
143
- * const counts = Object.values(current._entities).reduce((acc, entity) => {
144
- * acc[entity.status] = (acc[entity.status] || 0) + 1;
145
- * return acc;
146
- * }, {});
139
+ * const aggregated = Object.values(current._entities).reduce(...);
147
140
  *
148
- * 4. Return with internal state:
149
- * return { ...publicFields, _entities: current._entities } as Internal<%= pascalCase(targetName || 'State') %>;
141
+ * 4. Return via typed variable (preserves _entities for next event):
142
+ * const result: Internal<%= pascalCase(targetName || 'State') %> = { ...publicFields, _entities: current._entities };
143
+ * return result;
150
144
  <% } else if (isCompositeKey) { -%>
151
145
  * **COMPOSITE KEY PROJECTION**
152
146
  *