@auto-engineer/server-generator-apollo-emmett 0.2.0 → 0.8.2

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 (76) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-test.log +5 -108
  3. package/.turbo/turbo-type-check.log +1 -1
  4. package/CHANGELOG.md +15 -0
  5. package/dist/codegen/extract/commands.d.ts +11 -5
  6. package/dist/codegen/extract/commands.d.ts.map +1 -1
  7. package/dist/codegen/extract/commands.js +27 -12
  8. package/dist/codegen/extract/commands.js.map +1 -1
  9. package/dist/codegen/extract/data-sink.d.ts.map +1 -1
  10. package/dist/codegen/extract/data-sink.js +52 -28
  11. package/dist/codegen/extract/data-sink.js.map +1 -1
  12. package/dist/codegen/extract/gwt.d.ts.map +1 -1
  13. package/dist/codegen/extract/gwt.js +37 -6
  14. package/dist/codegen/extract/gwt.js.map +1 -1
  15. package/dist/codegen/extract/messages.d.ts.map +1 -1
  16. package/dist/codegen/extract/messages.js +38 -6
  17. package/dist/codegen/extract/messages.js.map +1 -1
  18. package/dist/codegen/extract/query.d.ts.map +1 -1
  19. package/dist/codegen/extract/query.js +9 -2
  20. package/dist/codegen/extract/query.js.map +1 -1
  21. package/dist/codegen/scaffoldFromSchema.d.ts.map +1 -1
  22. package/dist/codegen/scaffoldFromSchema.js +21 -6
  23. package/dist/codegen/scaffoldFromSchema.js.map +1 -1
  24. package/dist/codegen/templates/command/commands.specs.ts +28 -19
  25. package/dist/codegen/templates/command/decide.specs.specs.ts +75 -57
  26. package/dist/codegen/templates/command/decide.specs.ts +147 -110
  27. package/dist/codegen/templates/command/events.specs.ts +38 -29
  28. package/dist/codegen/templates/command/evolve.specs.ts +33 -24
  29. package/dist/codegen/templates/command/handle.specs.ts +61 -43
  30. package/dist/codegen/templates/command/mutation.resolver.specs.ts +28 -19
  31. package/dist/codegen/templates/command/register.specs.ts +34 -25
  32. package/dist/codegen/templates/command/state.specs.ts +34 -25
  33. package/dist/codegen/templates/query/projection.specs.specs..ts +100 -80
  34. package/dist/codegen/templates/query/projection.specs.ts +101 -81
  35. package/dist/codegen/templates/query/query.resolver.specs.ts +2 -2
  36. package/dist/codegen/templates/query/state.specs.ts +1 -1
  37. package/dist/codegen/templates/react/react.specs.specs.ts +86 -68
  38. package/dist/codegen/templates/react/react.specs.ts +123 -96
  39. package/dist/codegen/templates/react/react.specs.ts.ejs +9 -1
  40. package/dist/codegen/templates/react/react.ts.ejs +8 -1
  41. package/dist/codegen/templates/react/register.specs.ts +123 -96
  42. package/dist/codegen/templates/react/register.ts.ejs +8 -1
  43. package/dist/codegen/test-data/specVariant1.d.ts.map +1 -1
  44. package/dist/codegen/test-data/specVariant1.js +92 -65
  45. package/dist/codegen/test-data/specVariant1.js.map +1 -1
  46. package/dist/codegen/types.d.ts +4 -4
  47. package/dist/codegen/types.d.ts.map +1 -1
  48. package/package.json +9 -9
  49. package/src/codegen/extract/commands.ts +48 -19
  50. package/src/codegen/extract/data-sink.ts +68 -31
  51. package/src/codegen/extract/gwt.ts +45 -10
  52. package/src/codegen/extract/messages.ts +49 -7
  53. package/src/codegen/extract/query.ts +15 -2
  54. package/src/codegen/scaffoldFromSchema.ts +27 -6
  55. package/src/codegen/templates/command/commands.specs.ts +28 -19
  56. package/src/codegen/templates/command/decide.specs.specs.ts +75 -57
  57. package/src/codegen/templates/command/decide.specs.ts +147 -110
  58. package/src/codegen/templates/command/events.specs.ts +38 -29
  59. package/src/codegen/templates/command/evolve.specs.ts +33 -24
  60. package/src/codegen/templates/command/handle.specs.ts +61 -43
  61. package/src/codegen/templates/command/mutation.resolver.specs.ts +28 -19
  62. package/src/codegen/templates/command/register.specs.ts +34 -25
  63. package/src/codegen/templates/command/state.specs.ts +34 -25
  64. package/src/codegen/templates/query/projection.specs.specs..ts +100 -80
  65. package/src/codegen/templates/query/projection.specs.ts +101 -81
  66. package/src/codegen/templates/query/query.resolver.specs.ts +2 -2
  67. package/src/codegen/templates/query/state.specs.ts +1 -1
  68. package/src/codegen/templates/react/react.specs.specs.ts +86 -68
  69. package/src/codegen/templates/react/react.specs.ts +123 -96
  70. package/src/codegen/templates/react/react.specs.ts.ejs +9 -1
  71. package/src/codegen/templates/react/react.ts.ejs +8 -1
  72. package/src/codegen/templates/react/register.specs.ts +123 -96
  73. package/src/codegen/templates/react/register.ts.ejs +8 -1
  74. package/src/codegen/test-data/specVariant1.ts +92 -65
  75. package/src/codegen/types.ts +4 -4
  76. package/tsconfig.tsbuildinfo +1 -1
@@ -1,7 +1,6 @@
1
1
  import { CommandExample, EventExample } from '@auto-engineer/flow';
2
2
  import { Message, MessageDefinition } from '../types';
3
3
  import { extractFieldsFromMessage } from './fields';
4
- import { ReactGwtSpec } from './messages';
5
4
 
6
5
  function createCommandMessage(
7
6
  commandRef: string,
@@ -23,9 +22,9 @@ function createCommandMessage(
23
22
 
24
23
  export function extractCommandsFromGwt(
25
24
  gwtSpecs: Array<{
26
- given?: EventExample[];
27
- when: CommandExample;
28
- then: Array<EventExample | { errorType: string; message?: string }>;
25
+ given?: Array<EventExample | unknown>;
26
+ when: CommandExample | EventExample | unknown[];
27
+ then: Array<EventExample | unknown | { errorType: string; message?: string }>;
29
28
  }>,
30
29
  allMessages: MessageDefinition[],
31
30
  ): { commands: Message[]; commandSchemasByName: Record<string, Message> } {
@@ -34,8 +33,15 @@ export function extractCommandsFromGwt(
34
33
  const commands: Message[] = gwtSpecs
35
34
  .map((gwt): Message | undefined => {
36
35
  const cmd = gwt.when;
37
- if (!cmd.commandRef) return undefined;
38
-
36
+ if (
37
+ Array.isArray(cmd) ||
38
+ typeof cmd !== 'object' ||
39
+ cmd === null ||
40
+ !('commandRef' in cmd) ||
41
+ typeof cmd.commandRef !== 'string' ||
42
+ !cmd.commandRef
43
+ )
44
+ return undefined;
39
45
  const command = createCommandMessage(cmd.commandRef, allMessages, 'when');
40
46
  if (command) {
41
47
  commandSchemasByName[cmd.commandRef] = command;
@@ -47,8 +53,42 @@ export function extractCommandsFromGwt(
47
53
  return { commands, commandSchemasByName };
48
54
  }
49
55
 
56
+ function isValidCommandExample(commandExample: unknown): commandExample is { commandRef: string } {
57
+ return (
58
+ typeof commandExample === 'object' &&
59
+ commandExample !== null &&
60
+ 'commandRef' in commandExample &&
61
+ typeof commandExample.commandRef === 'string' &&
62
+ !!commandExample.commandRef
63
+ );
64
+ }
65
+
66
+ function processCommandExample(
67
+ commandExample: unknown,
68
+ commands: Message[],
69
+ commandSchemasByName: Record<string, Message>,
70
+ allMessages: MessageDefinition[],
71
+ ): void {
72
+ if (!isValidCommandExample(commandExample)) {
73
+ return;
74
+ }
75
+
76
+ const commandRef = commandExample.commandRef;
77
+ if (!(commandRef in commandSchemasByName)) {
78
+ const command = createCommandMessage(commandRef, allMessages, 'then');
79
+ if (command) {
80
+ commands.push(command);
81
+ commandSchemasByName[commandRef] = command;
82
+ }
83
+ }
84
+ }
85
+
50
86
  export function extractCommandsFromThen(
51
- gwtSpecs: ReactGwtSpec[],
87
+ gwtSpecs: Array<{
88
+ given?: Array<EventExample | unknown>;
89
+ when: CommandExample | EventExample | unknown[];
90
+ then: Array<EventExample | unknown | { errorType: string; message?: string }>;
91
+ }>,
52
92
  allMessages: MessageDefinition[],
53
93
  ): { commands: Message[]; commandSchemasByName: Record<string, Message> } {
54
94
  const commands: Message[] = [];
@@ -60,18 +100,7 @@ export function extractCommandsFromThen(
60
100
  }
61
101
 
62
102
  for (const commandExample of gwt.then) {
63
- if (!commandExample.commandRef) {
64
- continue;
65
- }
66
-
67
- const commandRef = commandExample.commandRef;
68
- if (!(commandRef in commandSchemasByName)) {
69
- const command = createCommandMessage(commandRef, allMessages, 'then');
70
- if (command) {
71
- commands.push(command);
72
- commandSchemasByName[commandRef] = command;
73
- }
74
- }
103
+ processCommandExample(commandExample, commands, commandSchemasByName, allMessages);
75
104
  }
76
105
  }
77
106
 
@@ -1,45 +1,82 @@
1
- import { CommandExample, DataSink, Slice } from '@auto-engineer/flow';
2
- import { CommandGwtSpec, QueryGwtSpec, ReactGwtSpec } from './messages';
1
+ import { CommandExample, DataSink, Slice, type Example } from '@auto-engineer/flow';
3
2
 
4
3
  function resolveStreamId(stream: string, exampleData: Record<string, unknown>): string {
5
4
  return stream.replace(/\$\{([^}]+)\}/g, (_, key: string) => String(exampleData?.[key] ?? 'unknown'));
6
5
  }
7
6
 
8
- // eslint-disable-next-line complexity
9
- export function getStreamFromSink(slice: Slice): { streamPattern?: string; streamId?: string } {
10
- let streamPattern: string | undefined;
11
- let streamId: string | undefined;
12
- const gwtSpecs =
13
- slice.type === 'command'
14
- ? (slice.server?.gwt as CommandGwtSpec[])
15
- : slice.type === 'query'
16
- ? (slice.server?.gwt as QueryGwtSpec[])
17
- : slice.type === 'react'
18
- ? (slice.server?.gwt as ReactGwtSpec[])
19
- : [];
20
- let exampleData: Record<string, unknown> = {};
7
+ function extractExampleDataFromReact(firstSpec: { when: unknown }): Record<string, unknown> {
8
+ if (Array.isArray(firstSpec.when)) {
9
+ const firstWhen = firstSpec.when[0] as { exampleData?: Record<string, unknown> } | undefined;
10
+ return typeof firstWhen?.exampleData === 'object' && firstWhen.exampleData !== null ? firstWhen.exampleData : {};
11
+ }
12
+ return {};
13
+ }
14
+
15
+ function extractExampleDataFromCommand(firstSpec: { then: unknown }): Record<string, unknown> {
16
+ const then = firstSpec.then as (CommandExample | { errorType: string })[];
17
+ const firstExample = then.find((t): t is CommandExample => 'exampleData' in t);
18
+ return typeof firstExample?.exampleData === 'object' && firstExample.exampleData !== null
19
+ ? firstExample.exampleData
20
+ : {};
21
+ }
22
+
23
+ function extractExampleDataFromQuery(firstSpec: { when: unknown }): Record<string, unknown> {
24
+ if (Array.isArray(firstSpec.when)) {
25
+ const firstWhen = firstSpec.when[0] as { exampleData?: Record<string, unknown> } | undefined;
26
+ return typeof firstWhen?.exampleData === 'object' && firstWhen.exampleData !== null ? firstWhen.exampleData : {};
27
+ }
28
+ return {};
29
+ }
30
+
31
+ function extractExampleDataFromSpecs(
32
+ slice: Slice,
33
+ gwtSpecs: Array<{ given?: unknown; when: unknown; then: unknown }>,
34
+ ): Record<string, unknown> {
35
+ if (gwtSpecs.length === 0) {
36
+ return {};
37
+ }
38
+
39
+ const firstSpec = gwtSpecs[0];
21
40
  switch (slice.type) {
22
41
  case 'react':
23
- exampleData = (gwtSpecs as ReactGwtSpec[])[0].when?.[0]?.exampleData ?? {};
24
- break;
25
- case 'command': {
26
- const then = (gwtSpecs as CommandGwtSpec[])[0]?.then as (CommandExample | { errorType: string })[];
27
- const firstExample = then.find((t): t is CommandExample => 'exampleData' in t);
28
- exampleData = firstExample?.exampleData ?? {};
29
- break;
30
- }
42
+ return extractExampleDataFromReact(firstSpec);
43
+ case 'command':
44
+ return extractExampleDataFromCommand(firstSpec);
31
45
  case 'query':
32
- exampleData = (gwtSpecs as QueryGwtSpec[])[0]?.given?.[0]?.exampleData ?? {};
33
- break;
46
+ return extractExampleDataFromQuery(firstSpec);
47
+ default:
48
+ return {};
34
49
  }
35
- const sink = (slice.server?.data ?? []).find((item) => (item as DataSink)?.destination?.type === 'stream') as
50
+ }
51
+
52
+ function findStreamSink(slice: Slice): DataSink | undefined {
53
+ return (slice.server?.data ?? []).find((item) => (item as DataSink)?.destination?.type === 'stream') as
36
54
  | DataSink
37
55
  | undefined;
56
+ }
57
+
58
+ export function getStreamFromSink(slice: Slice): { streamPattern?: string; streamId?: string } {
59
+ const specs = slice.server?.specs;
60
+ const rules = specs?.rules;
61
+ const gwtSpecs =
62
+ Array.isArray(rules) && rules.length > 0
63
+ ? rules.flatMap((rule) =>
64
+ rule.examples.map((example: Example) => ({
65
+ given: example.given,
66
+ when: example.when,
67
+ then: example.then,
68
+ })),
69
+ )
70
+ : [];
71
+
72
+ const exampleData = extractExampleDataFromSpecs(slice, gwtSpecs);
73
+ const sink = findStreamSink(slice);
74
+
38
75
  if (sink && sink.destination.type === 'stream' && 'pattern' in sink.destination) {
39
- streamPattern = sink.destination.pattern;
40
- if (streamPattern != null) {
41
- streamId = resolveStreamId(streamPattern, exampleData);
42
- }
76
+ const streamPattern = sink.destination.pattern;
77
+ const streamId = streamPattern ? resolveStreamId(streamPattern, exampleData) : undefined;
78
+ return { streamPattern, streamId };
43
79
  }
44
- return { streamPattern, streamId };
80
+
81
+ return {};
45
82
  }
@@ -1,4 +1,4 @@
1
- import { Slice } from '@auto-engineer/flow';
1
+ import { Slice, CommandExample, EventExample, StateExample } from '@auto-engineer/flow';
2
2
  import { GwtCondition } from '../types';
3
3
 
4
4
  export function buildCommandGwtMapping(slice: Slice): Record<string, (GwtCondition & { failingFields?: string[] })[]> {
@@ -6,21 +6,50 @@ export function buildCommandGwtMapping(slice: Slice): Record<string, (GwtConditi
6
6
  return {};
7
7
  }
8
8
 
9
- const gwtSpecs = slice.server?.gwt ?? [];
9
+ const gwtSpecs = extractGwtSpecs(slice);
10
+ const mapping = buildCommandMapping(gwtSpecs);
11
+ return enhanceMapping(mapping);
12
+ }
13
+
14
+ function extractGwtSpecs(slice: Slice) {
15
+ const specs = slice.server?.specs;
16
+ const rules = specs?.rules;
17
+ return Array.isArray(rules) && rules.length > 0
18
+ ? rules.flatMap((rule) =>
19
+ rule.examples.map((example) => ({
20
+ given: example.given,
21
+ when: example.when,
22
+ then: example.then,
23
+ })),
24
+ )
25
+ : [];
26
+ }
27
+
28
+ function buildCommandMapping(gwtSpecs: Array<{ given: unknown; when: unknown; then: unknown }>) {
10
29
  const mapping: Record<string, GwtCondition[]> = {};
11
30
 
12
31
  for (const gwt of gwtSpecs) {
13
- const command = gwt.when?.commandRef;
14
- if (command) {
32
+ let command: string | undefined;
33
+ if (Array.isArray(gwt.when)) {
34
+ continue;
35
+ } else {
36
+ const whenCmd = gwt.when as { commandRef?: string } | undefined;
37
+ command = whenCmd?.commandRef;
38
+ }
39
+ if (typeof command === 'string' && command.length > 0) {
15
40
  mapping[command] = mapping[command] ?? [];
16
41
  mapping[command].push({
17
- given: gwt.given,
18
- when: gwt.when,
19
- then: gwt.then,
42
+ given: gwt.given as Array<EventExample | StateExample> | undefined,
43
+ when: gwt.when as CommandExample | EventExample[],
44
+ then: gwt.then as Array<EventExample | StateExample | CommandExample | { errorType: string; message?: string }>,
20
45
  });
21
46
  }
22
47
  }
23
48
 
49
+ return mapping;
50
+ }
51
+
52
+ function enhanceMapping(mapping: Record<string, GwtCondition[]>) {
24
53
  const enhancedMapping: Record<string, (GwtCondition & { failingFields?: string[] })[]> = {};
25
54
 
26
55
  for (const command in mapping) {
@@ -40,7 +69,9 @@ function mergeGwtConditions(gwts: GwtCondition[]): GwtCondition[] {
40
69
  const map = new Map<string, GwtCondition[]>();
41
70
 
42
71
  for (const gwt of gwts) {
43
- const key = JSON.stringify(gwt.when.exampleData);
72
+ // Handle both single command and array of events in when clause
73
+ const whenData = Array.isArray(gwt.when) ? gwt.when[0]?.exampleData : gwt.when.exampleData;
74
+ const key = JSON.stringify(whenData ?? {});
44
75
  const existing = map.get(key) ?? [];
45
76
  map.set(key, [...existing, gwt]);
46
77
  }
@@ -58,7 +89,8 @@ function mergeGwtConditions(gwts: GwtCondition[]): GwtCondition[] {
58
89
 
59
90
  function findSuccessfulExampleData(gwts: GwtCondition[]): Record<string, unknown> {
60
91
  const successful = gwts.find((gwt) => gwt.then.some((t) => typeof t === 'object' && t !== null && 'eventRef' in t));
61
- return successful?.when.exampleData ?? {};
92
+ const whenData = Array.isArray(successful?.when) ? successful?.when[0]?.exampleData : successful?.when?.exampleData;
93
+ return typeof whenData === 'object' && whenData !== null ? whenData : {};
62
94
  }
63
95
 
64
96
  function findFailingFields(gwt: GwtCondition, successfulData: Record<string, unknown>): string[] {
@@ -66,7 +98,10 @@ function findFailingFields(gwt: GwtCondition, successfulData: Record<string, unk
66
98
 
67
99
  if (!hasError) return [];
68
100
 
69
- return Object.entries(gwt.when.exampleData)
101
+ const whenData = Array.isArray(gwt.when) ? gwt.when[0]?.exampleData : gwt.when?.exampleData;
102
+ if (typeof whenData !== 'object' || whenData === null) return [];
103
+
104
+ return Object.entries(whenData)
70
105
  .filter(([key, val]) => {
71
106
  const successVal = successfulData[key];
72
107
  return val === '' && successVal !== '' && successVal !== undefined;
@@ -67,7 +67,18 @@ function extractMessagesForCommand(slice: Slice, allMessages: MessageDefinition[
67
67
  return EMPTY_EXTRACTED_MESSAGES;
68
68
  }
69
69
 
70
- const gwtSpecs = slice.server?.gwt ?? [];
70
+ const specs = slice.server?.specs;
71
+ const rules = specs?.rules;
72
+ const gwtSpecs =
73
+ Array.isArray(rules) && rules.length > 0
74
+ ? rules.flatMap((rule) =>
75
+ rule.examples.map((example) => ({
76
+ given: example.given,
77
+ when: example.when,
78
+ then: example.then,
79
+ })),
80
+ )
81
+ : [];
71
82
  debugCommand(' Found %d GWT specs', gwtSpecs.length);
72
83
 
73
84
  const { commands, commandSchemasByName } = extractCommandsFromGwt(gwtSpecs, allMessages);
@@ -75,8 +86,11 @@ function extractMessagesForCommand(slice: Slice, allMessages: MessageDefinition[
75
86
  debugCommand(' Command schemas: %o', Object.keys(commandSchemasByName));
76
87
 
77
88
  const events: Message[] = gwtSpecs.flatMap((gwt): Message[] => {
78
- const givenEvents = extractEventsFromGiven(gwt.given, allMessages);
79
- const thenEvents = extractEventsFromThen(gwt.then, allMessages);
89
+ const givenEventsOnly = gwt.given?.filter((item): item is EventExample => 'eventRef' in item);
90
+ const givenEvents = extractEventsFromGiven(givenEventsOnly, allMessages);
91
+
92
+ const thenEventsOnly = gwt.then.filter((item): item is EventExample => 'eventRef' in item);
93
+ const thenEvents = extractEventsFromThen(thenEventsOnly, allMessages);
80
94
  debugCommand(' GWT: given=%d events, then=%d events', givenEvents.length, thenEvents.length);
81
95
  return [...givenEvents, ...thenEvents];
82
96
  });
@@ -101,13 +115,29 @@ function extractMessagesForQuery(slice: Slice, allMessages: MessageDefinition[])
101
115
  return EMPTY_EXTRACTED_MESSAGES;
102
116
  }
103
117
 
104
- const gwtSpecs = slice.server?.gwt ?? [];
118
+ const specs = slice.server?.specs;
119
+ const rules = specs?.rules;
120
+ const gwtSpecs =
121
+ Array.isArray(rules) && rules.length > 0
122
+ ? rules.flatMap((rule) =>
123
+ rule.examples.map((example) => ({
124
+ given: example.given,
125
+ when: example.when,
126
+ then: example.then,
127
+ })),
128
+ )
129
+ : [];
105
130
  debugQuery(' Found %d GWT specs', gwtSpecs.length);
106
131
 
107
132
  const projectionIdField = extractProjectionIdField(slice);
108
133
  debugQuery(' Projection ID field: %s', projectionIdField ?? 'none');
109
134
 
110
- const events: Message[] = gwtSpecs.flatMap((gwt) => extractEventsFromGiven(gwt.given, allMessages));
135
+ const events: Message[] = gwtSpecs.flatMap((gwt) => {
136
+ const eventsFromWhen = Array.isArray(gwt.when)
137
+ ? gwt.when.filter((item): item is EventExample => 'eventRef' in item)
138
+ : [];
139
+ return extractEventsFromGiven(eventsFromWhen, allMessages);
140
+ });
111
141
  debugQuery(' Extracted %d events from given', events.length);
112
142
 
113
143
  const states: Message[] = extractStatesFromTarget(slice, allMessages);
@@ -133,10 +163,22 @@ function extractMessagesForReact(slice: Slice, allMessages: MessageDefinition[])
133
163
  return EMPTY_EXTRACTED_MESSAGES;
134
164
  }
135
165
 
136
- const gwtSpecs = slice.server?.gwt ?? [];
166
+ const specs = slice.server?.specs;
167
+ // Extract all examples from specs/rules structure
168
+ const rules = specs?.rules;
169
+ const gwtSpecs =
170
+ Array.isArray(rules) && rules.length > 0
171
+ ? rules.flatMap((rule) =>
172
+ rule.examples.map((example) => ({
173
+ given: example.given,
174
+ when: example.when,
175
+ then: example.then,
176
+ })),
177
+ )
178
+ : [];
137
179
  debugReact(' Found %d GWT specs', gwtSpecs.length);
138
180
 
139
- const events = extractEventsFromWhen(gwtSpecs, allMessages);
181
+ const events = extractEventsFromWhen(gwtSpecs as ReactGwtSpec[], allMessages);
140
182
  debugReact(' Extracted %d events from when', events.length);
141
183
 
142
184
  const { commands, commandSchemasByName } = extractCommandsFromThen(gwtSpecs, allMessages);
@@ -10,9 +10,22 @@ export function buildQueryGwtMapping(slice: Slice): QueryGwtCondition[] {
10
10
  return [];
11
11
  }
12
12
 
13
- const gwtSpecs = slice.server?.gwt ?? [];
13
+ const specs = slice.server?.specs;
14
+ const rules = specs?.rules;
15
+ const gwtSpecs =
16
+ Array.isArray(rules) && rules.length > 0
17
+ ? rules.flatMap((rule) =>
18
+ rule.examples.map((example) => ({
19
+ given: Array.isArray(example.when) ? example.when : [], // For query slices, when contains the given events
20
+ then: example.then,
21
+ })),
22
+ )
23
+ : [];
24
+
14
25
  return gwtSpecs.map((gwt) => ({
15
26
  given: gwt.given,
16
- then: gwt.then,
27
+ then: gwt.then.filter(
28
+ (item): item is { stateRef: string; exampleData: Record<string, unknown> } => 'stateRef' in item,
29
+ ),
17
30
  }));
18
31
  }
@@ -272,8 +272,19 @@ function findEventSource(flows: Flow[], eventType: string): { flowName: string;
272
272
  for (const slice of flow.slices) {
273
273
  if (!['command', 'react'].includes(slice.type)) continue;
274
274
 
275
- const gwt = slice.server?.gwt ?? [];
276
- if (gwt.some((g) => g.then.some((t) => 'eventRef' in t && t.eventRef === eventType))) {
275
+ const specs = slice.server?.specs;
276
+ const rules = specs?.rules;
277
+ const gwtSpecs =
278
+ Array.isArray(rules) && rules.length > 0
279
+ ? rules.flatMap((rule) =>
280
+ rule.examples.map((example) => ({
281
+ given: example.given,
282
+ when: example.when,
283
+ then: example.then,
284
+ })),
285
+ )
286
+ : [];
287
+ if (gwtSpecs.some((g) => g.then.some((t) => 'eventRef' in t && t.eventRef === eventType))) {
277
288
  debugSlice(' Found event source in flow: %s, slice: %s', flow.name, slice.name);
278
289
  return { flowName: flow.name, sliceName: slice.name };
279
290
  }
@@ -304,8 +315,19 @@ function findCommandSource(flows: Flow[], commandType: string): { flowName: stri
304
315
  for (const slice of flow.slices) {
305
316
  if (slice.type !== 'command') continue;
306
317
 
307
- const gwt = slice.server?.gwt ?? [];
308
- if (gwt.some((g) => 'commandRef' in g.when && g.when.commandRef === commandType)) {
318
+ const specs = slice.server?.specs;
319
+ const rules = specs?.rules;
320
+ const gwtSpecs =
321
+ Array.isArray(rules) && rules.length > 0
322
+ ? rules.flatMap((rule) =>
323
+ rule.examples.map((example) => ({
324
+ given: example.given,
325
+ when: example.when,
326
+ then: example.then,
327
+ })),
328
+ )
329
+ : [];
330
+ if (gwtSpecs.some((g) => !Array.isArray(g.when) && 'commandRef' in g.when && g.when.commandRef === commandType)) {
309
331
  debugSlice(' Found command source in flow: %s, slice: %s', flow.name, slice.name);
310
332
  return { flowName: flow.name, sliceName: slice.name };
311
333
  }
@@ -339,7 +361,7 @@ async function generateFilesForSlice(
339
361
  debugSlice(' Commands: %d', extracted.commands.length);
340
362
  debugSlice(' Events: %d', extracted.events.length);
341
363
  debugSlice(' States: %d', extracted.states.length);
342
- console.log(
364
+ debugSlice(
343
365
  '💡 Events for slice',
344
366
  slice.name,
345
367
  extracted.events.map((e) => e.type),
@@ -416,7 +438,6 @@ export async function writeScaffoldFilePlans(plans: FilePlan[]) {
416
438
  await fs.writeFile(outputPath, contents, 'utf8');
417
439
  writtenCount++;
418
440
  debugPlan(' File written successfully (%d/%d)', writtenCount, plans.length);
419
- console.log(`✅ Created: ${outputPath}`);
420
441
  }
421
442
 
422
443
  debugPlan('All %d files written successfully', writtenCount);
@@ -19,26 +19,35 @@ describe('commands.ts.ejs', () => {
19
19
  },
20
20
  server: {
21
21
  description: 'test',
22
- gwt: [
23
- {
24
- when: {
25
- commandRef: 'CreateListing',
26
- exampleData: {
27
- propertyId: 'listing_123',
28
- title: 'nice apartment',
29
- pricePerNight: 250,
30
- maxGuests: 4,
31
- amenities: ['wifi', 'kitchen', 'parking'],
32
- available: true,
33
- tags: ['sea view', 'balcony'],
34
- rating: 4.8,
35
- metadata: { petsAllowed: true },
36
- listedAt: '2024-01-15T10:00:00Z',
37
- },
22
+ specs: {
23
+ name: 'Create listing command',
24
+ rules: [
25
+ {
26
+ description: 'Should accept valid listing data',
27
+ examples: [
28
+ {
29
+ description: 'User creates listing with valid data',
30
+ when: {
31
+ commandRef: 'CreateListing',
32
+ exampleData: {
33
+ propertyId: 'listing_123',
34
+ title: 'nice apartment',
35
+ pricePerNight: 250,
36
+ maxGuests: 4,
37
+ amenities: ['wifi', 'kitchen', 'parking'],
38
+ available: true,
39
+ tags: ['sea view', 'balcony'],
40
+ rating: 4.8,
41
+ metadata: { petsAllowed: true },
42
+ listedAt: '2024-01-15T10:00:00Z',
43
+ },
44
+ },
45
+ then: [],
46
+ },
47
+ ],
38
48
  },
39
- then: [],
40
- },
41
- ],
49
+ ],
50
+ },
42
51
  },
43
52
  },
44
53
  ],