@auto-engineer/server-generator-apollo-emmett 1.83.0 → 1.84.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.84.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.83.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  18:35:37
13
+  Duration  30.90s (transform 4.71s, setup 0ms, collect 65.46s, tests 12.81s, environment 7ms, prepare 5.24s)
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.83.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,29 @@
1
1
  # @auto-engineer/server-generator-apollo-emmett
2
2
 
3
+ ## 1.84.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`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
8
+ - **server-implementer**: add detectImportedTypeShadowing utility
9
+ - **server-generator-nestjs**: add model to ServerGenerationFailedEvent
10
+ - **server-generator-apollo-emmett**: add model to failure events
11
+ - **pipeline**: emit PipelineRunCompleted when all commands complete
12
+
13
+ - [`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
14
+
15
+ ### Patch Changes
16
+
17
+ - [`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
18
+
19
+ - [`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
20
+
21
+ - [`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
22
+
23
+ - 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)]:
24
+ - @auto-engineer/message-bus@1.84.0
25
+ - @auto-engineer/narrative@1.84.0
26
+
3
27
  ## 1.83.0
4
28
 
5
29
  ### 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":"AAEA,OAAO,EAAE,wBAAwB,EAAkD,MAAM,kBAAkB,CAAC;AAE5G,MAAM,UAAU,sBAAsB,CAAC,KAAY;IACjD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,QAAQ,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAC9C,OAAO,cAAc,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,YAAY,CAAC,IAA8C;IAClE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,YAAY,IAAI,IAAI,CAAC;AACtD,CAAC;AAED,SAAS,mBAAmB,CAAC,QAAgC;IAC3D,MAAM,OAAO,GAAmC,EAAE,CAAC;IAEnD,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5B,SAAS;QACX,CAAC;QACD,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC;QACpC,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtD,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAC1C,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC;gBACpB,KAAK,EAAE,GAAG,CAAC,KAAK;gBAChB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,WAAW,EAAE,GAAG,CAAC,WAAW;gBAC5B,eAAe,EAAE,GAAG,CAAC,eAAe;aACrC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,KAAK,MAAM,OAAO,IAAI,OAAO,EAAE,CAAC;QAC9B,OAAO,CAAC,OAAO,CAAC,GAAG,uBAAuB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/D,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,UAA0B;IAChE,MAAM,MAAM,GAAmB,EAAE,CAAC;IAClC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE7C,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,WAAW,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;QACjE,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACvB,SAAS;QACX,CAAC;QAED,MAAM,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;QACjC,MAAM,WAAW,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAExC,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC9B,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;YACrC,KAAK,MAAM,CAAC,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC;gBAC/B,IAAI,UAAU,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAChG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBACxB,CAAC;YACH,CAAC;QACH,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;YACnC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,SAAS,EAAE,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,SAAS,CAAC,GAAiB;IAClC,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACnE,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7C,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9E,CAAC,CAAC,CAAC,CACN,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,OAAuC;IAC7D,MAAM,eAAe,GAAoE,EAAE,CAAC;IAE5F,KAAK,MAAM,OAAO,IAAI,OAAO,EAAE,CAAC;QAC9B,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,cAAc,GAAG,yBAAyB,CAAC,UAAU,CAAC,CAAC;QAE7D,eAAe,CAAC,OAAO,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAClD,GAAG,GAAG;YACN,aAAa,EAAE,iBAAiB,CAAC,GAAG,EAAE,cAAc,CAAC;SACtD,CAAC,CAAC,CAAC;IACN,CAAC;IAED,OAAO,eAAe,CAAC;AACzB,CAAC;AAED,SAAS,yBAAyB,CAAC,IAAoB;IACrD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC;IACpH,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,UAAU,EAAE,IAAI,EAAE,WAAW,CAAC;IACpH,OAAO,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;AAC3E,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAiB,EAAE,cAAuC;IACnF,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,WAAW,IAAI,CAAC,CAAC,CAAC;IAE/F,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAC;IAEzB,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,WAAW,CAAC;IAC5F,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IAEjE,OAAO,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;SAC5B,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE;QACrB,MAAM,UAAU,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QACvC,OAAO,GAAG,KAAK,EAAE,IAAI,UAAU,KAAK,EAAE,IAAI,UAAU,KAAK,SAAS,CAAC;IACrE,CAAC,CAAC;SACD,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC"}
@@ -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
  *