@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.
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +5 -108
- package/.turbo/turbo-type-check.log +1 -1
- package/CHANGELOG.md +15 -0
- package/dist/codegen/extract/commands.d.ts +11 -5
- package/dist/codegen/extract/commands.d.ts.map +1 -1
- package/dist/codegen/extract/commands.js +27 -12
- package/dist/codegen/extract/commands.js.map +1 -1
- package/dist/codegen/extract/data-sink.d.ts.map +1 -1
- package/dist/codegen/extract/data-sink.js +52 -28
- package/dist/codegen/extract/data-sink.js.map +1 -1
- package/dist/codegen/extract/gwt.d.ts.map +1 -1
- package/dist/codegen/extract/gwt.js +37 -6
- package/dist/codegen/extract/gwt.js.map +1 -1
- package/dist/codegen/extract/messages.d.ts.map +1 -1
- package/dist/codegen/extract/messages.js +38 -6
- package/dist/codegen/extract/messages.js.map +1 -1
- package/dist/codegen/extract/query.d.ts.map +1 -1
- package/dist/codegen/extract/query.js +9 -2
- package/dist/codegen/extract/query.js.map +1 -1
- package/dist/codegen/scaffoldFromSchema.d.ts.map +1 -1
- package/dist/codegen/scaffoldFromSchema.js +21 -6
- package/dist/codegen/scaffoldFromSchema.js.map +1 -1
- package/dist/codegen/templates/command/commands.specs.ts +28 -19
- package/dist/codegen/templates/command/decide.specs.specs.ts +75 -57
- package/dist/codegen/templates/command/decide.specs.ts +147 -110
- package/dist/codegen/templates/command/events.specs.ts +38 -29
- package/dist/codegen/templates/command/evolve.specs.ts +33 -24
- package/dist/codegen/templates/command/handle.specs.ts +61 -43
- package/dist/codegen/templates/command/mutation.resolver.specs.ts +28 -19
- package/dist/codegen/templates/command/register.specs.ts +34 -25
- package/dist/codegen/templates/command/state.specs.ts +34 -25
- package/dist/codegen/templates/query/projection.specs.specs..ts +100 -80
- package/dist/codegen/templates/query/projection.specs.ts +101 -81
- package/dist/codegen/templates/query/query.resolver.specs.ts +2 -2
- package/dist/codegen/templates/query/state.specs.ts +1 -1
- package/dist/codegen/templates/react/react.specs.specs.ts +86 -68
- package/dist/codegen/templates/react/react.specs.ts +123 -96
- package/dist/codegen/templates/react/react.specs.ts.ejs +9 -1
- package/dist/codegen/templates/react/react.ts.ejs +8 -1
- package/dist/codegen/templates/react/register.specs.ts +123 -96
- package/dist/codegen/templates/react/register.ts.ejs +8 -1
- package/dist/codegen/test-data/specVariant1.d.ts.map +1 -1
- package/dist/codegen/test-data/specVariant1.js +92 -65
- package/dist/codegen/test-data/specVariant1.js.map +1 -1
- package/dist/codegen/types.d.ts +4 -4
- package/dist/codegen/types.d.ts.map +1 -1
- package/package.json +9 -9
- package/src/codegen/extract/commands.ts +48 -19
- package/src/codegen/extract/data-sink.ts +68 -31
- package/src/codegen/extract/gwt.ts +45 -10
- package/src/codegen/extract/messages.ts +49 -7
- package/src/codegen/extract/query.ts +15 -2
- package/src/codegen/scaffoldFromSchema.ts +27 -6
- package/src/codegen/templates/command/commands.specs.ts +28 -19
- package/src/codegen/templates/command/decide.specs.specs.ts +75 -57
- package/src/codegen/templates/command/decide.specs.ts +147 -110
- package/src/codegen/templates/command/events.specs.ts +38 -29
- package/src/codegen/templates/command/evolve.specs.ts +33 -24
- package/src/codegen/templates/command/handle.specs.ts +61 -43
- package/src/codegen/templates/command/mutation.resolver.specs.ts +28 -19
- package/src/codegen/templates/command/register.specs.ts +34 -25
- package/src/codegen/templates/command/state.specs.ts +34 -25
- package/src/codegen/templates/query/projection.specs.specs..ts +100 -80
- package/src/codegen/templates/query/projection.specs.ts +101 -81
- package/src/codegen/templates/query/query.resolver.specs.ts +2 -2
- package/src/codegen/templates/query/state.specs.ts +1 -1
- package/src/codegen/templates/react/react.specs.specs.ts +86 -68
- package/src/codegen/templates/react/react.specs.ts +123 -96
- package/src/codegen/templates/react/react.specs.ts.ejs +9 -1
- package/src/codegen/templates/react/react.ts.ejs +8 -1
- package/src/codegen/templates/react/register.specs.ts +123 -96
- package/src/codegen/templates/react/register.ts.ejs +8 -1
- package/src/codegen/test-data/specVariant1.ts +92 -65
- package/src/codegen/types.ts +4 -4
- 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 (
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
33
|
-
|
|
46
|
+
return extractExampleDataFromQuery(firstSpec);
|
|
47
|
+
default:
|
|
48
|
+
return {};
|
|
34
49
|
}
|
|
35
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
76
|
+
const streamPattern = sink.destination.pattern;
|
|
77
|
+
const streamId = streamPattern ? resolveStreamId(streamPattern, exampleData) : undefined;
|
|
78
|
+
return { streamPattern, streamId };
|
|
43
79
|
}
|
|
44
|
-
|
|
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
|
|
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
|
-
|
|
14
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
79
|
-
const
|
|
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
|
|
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) =>
|
|
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
|
|
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
|
|
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
|
|
276
|
-
|
|
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
|
|
308
|
-
|
|
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
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
],
|
|
49
|
+
],
|
|
50
|
+
},
|
|
42
51
|
},
|
|
43
52
|
},
|
|
44
53
|
],
|