@auto-engineer/server-generator-apollo-emmett 1.138.0 → 1.140.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.
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +4 -4
- package/.turbo/turbo-type-check.log +1 -1
- package/CHANGELOG.md +39 -0
- package/DEBUG.md +4 -4
- package/dist/src/codegen/extract/data-sink.d.ts +2 -2
- package/dist/src/codegen/extract/data-sink.d.ts.map +1 -1
- package/dist/src/codegen/extract/data-sink.js +2 -2
- package/dist/src/codegen/extract/data-sink.js.map +1 -1
- package/dist/src/codegen/extract/events.d.ts +3 -3
- package/dist/src/codegen/extract/events.d.ts.map +1 -1
- package/dist/src/codegen/extract/events.js +11 -11
- package/dist/src/codegen/extract/events.js.map +1 -1
- package/dist/src/codegen/extract/gwt.d.ts +2 -2
- package/dist/src/codegen/extract/gwt.d.ts.map +1 -1
- package/dist/src/codegen/extract/gwt.js +2 -2
- package/dist/src/codegen/extract/gwt.js.map +1 -1
- package/dist/src/codegen/extract/imports.d.ts +4 -4
- package/dist/src/codegen/extract/imports.d.ts.map +1 -1
- package/dist/src/codegen/extract/imports.js +8 -8
- package/dist/src/codegen/extract/imports.js.map +1 -1
- package/dist/src/codegen/extract/messages.d.ts +2 -2
- package/dist/src/codegen/extract/messages.d.ts.map +1 -1
- package/dist/src/codegen/extract/messages.js +9 -9
- package/dist/src/codegen/extract/messages.js.map +1 -1
- package/dist/src/codegen/extract/projection.d.ts +7 -7
- package/dist/src/codegen/extract/projection.d.ts.map +1 -1
- package/dist/src/codegen/extract/projection.js +3 -3
- package/dist/src/codegen/extract/projection.js.map +1 -1
- package/dist/src/codegen/extract/query.d.ts +2 -2
- package/dist/src/codegen/extract/query.d.ts.map +1 -1
- package/dist/src/codegen/extract/query.js.map +1 -1
- package/dist/src/codegen/extract/slice-normalizer.d.ts +4 -4
- package/dist/src/codegen/extract/slice-normalizer.d.ts.map +1 -1
- package/dist/src/codegen/extract/slice-normalizer.js +7 -7
- package/dist/src/codegen/extract/slice-normalizer.js.map +1 -1
- package/dist/src/codegen/extract/states.d.ts +3 -3
- package/dist/src/codegen/extract/states.d.ts.map +1 -1
- package/dist/src/codegen/extract/states.js.map +1 -1
- package/dist/src/codegen/extract/step-converter.d.ts +7 -7
- package/dist/src/codegen/extract/step-converter.d.ts.map +1 -1
- package/dist/src/codegen/extract/step-converter.js +12 -12
- package/dist/src/codegen/extract/step-converter.js.map +1 -1
- package/dist/src/codegen/extract/step-types.d.ts +4 -4
- package/dist/src/codegen/extract/step-types.d.ts.map +1 -1
- package/dist/src/codegen/extract/step-types.js +1 -1
- package/dist/src/codegen/extract/step-types.js.map +1 -1
- package/dist/src/codegen/scaffoldFromSchema.d.ts +12 -12
- package/dist/src/codegen/scaffoldFromSchema.d.ts.map +1 -1
- package/dist/src/codegen/scaffoldFromSchema.js +113 -101
- package/dist/src/codegen/scaffoldFromSchema.js.map +1 -1
- package/dist/src/codegen/templates/command/commands.specs.ts +3 -3
- package/dist/src/codegen/templates/command/decide.specs.specs.ts +52 -52
- package/dist/src/codegen/templates/command/decide.specs.ts +12 -12
- package/dist/src/codegen/templates/command/decide.specs.ts.ejs +1 -1
- package/dist/src/codegen/templates/command/events.specs.ts +3 -3
- package/dist/src/codegen/templates/command/evolve.specs.ts +3 -3
- package/dist/src/codegen/templates/command/handle.specs.ts +13 -13
- package/dist/src/codegen/templates/command/mutation.resolver.specs.ts +19 -19
- package/dist/src/codegen/templates/command/register.specs.ts +3 -3
- package/dist/src/codegen/templates/command/state.specs.ts +3 -3
- package/dist/src/codegen/templates/query/events.specs.ts +4 -4
- package/dist/src/codegen/templates/query/projection.specs.specs.ts +60 -60
- package/dist/src/codegen/templates/query/projection.specs.ts +54 -29
- package/dist/src/codegen/templates/query/projection.specs.ts.ejs +2 -2
- package/dist/src/codegen/templates/query/query.resolver.specs.ts +63 -63
- package/dist/src/codegen/templates/query/query.resolver.ts.ejs +1 -1
- package/dist/src/codegen/templates/query/state.specs.ts +9 -9
- package/dist/src/codegen/templates/react/react.specs.specs.ts +15 -15
- package/dist/src/codegen/templates/react/react.specs.ts +16 -16
- package/dist/src/codegen/templates/react/react.specs.ts.ejs +9 -9
- package/dist/src/codegen/templates/react/react.ts.ejs +5 -5
- package/dist/src/codegen/templates/react/react.ts.specs.ts +33 -33
- package/dist/src/codegen/templates/react/register.specs.ts +7 -7
- package/dist/src/codegen/templates/react/register.ts.ejs +4 -4
- package/dist/src/codegen/test-data/specVariant1.d.ts.map +1 -1
- package/dist/src/codegen/test-data/specVariant1.js +3 -2
- package/dist/src/codegen/test-data/specVariant1.js.map +1 -1
- package/dist/src/codegen/types.d.ts +2 -2
- package/dist/src/codegen/types.d.ts.map +1 -1
- package/dist/src/commands/generate-server.d.ts +21 -21
- package/dist/src/commands/generate-server.d.ts.map +1 -1
- package/dist/src/commands/generate-server.js +81 -63
- package/dist/src/commands/generate-server.js.map +1 -1
- package/dist/src/commands/initialize-server.d.ts.map +1 -1
- package/dist/src/commands/initialize-server.js +2 -2
- package/dist/src/commands/initialize-server.js.map +1 -1
- package/dist/src/domain/flows/shared/types.d.ts +14 -0
- package/dist/src/domain/flows/shared/types.d.ts.map +1 -0
- package/dist/src/domain/flows/shared/types.js +2 -0
- package/dist/src/domain/flows/shared/types.js.map +1 -0
- package/dist/src/domain/flows/shared/types.ts +15 -0
- package/dist/src/domain/narratives/shared/types.d.ts +14 -0
- package/dist/src/domain/narratives/shared/types.d.ts.map +1 -0
- package/dist/src/domain/narratives/shared/types.js +2 -0
- package/dist/src/domain/narratives/shared/types.js.map +1 -0
- package/dist/src/domain/narratives/shared/types.ts +15 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/server.js +3 -3
- package/dist/src/server.js.map +1 -1
- package/dist/src/server.ts +3 -3
- package/dist/src/utils/loadRegisterFiles.d.ts +2 -2
- package/dist/src/utils/loadRegisterFiles.d.ts.map +1 -1
- package/dist/src/utils/loadRegisterFiles.js.map +1 -1
- package/dist/src/utils/loadRegisterFiles.ts +5 -5
- package/dist/src/utils/loadResolvers.js +1 -1
- package/dist/src/utils/loadResolvers.js.map +1 -1
- package/dist/src/utils/loadResolvers.ts +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/codegen/extract/data-sink.ts +5 -5
- package/src/codegen/extract/events.ts +15 -15
- package/src/codegen/extract/gwt.ts +4 -4
- package/src/codegen/extract/imports.specs.ts +19 -19
- package/src/codegen/extract/imports.ts +13 -13
- package/src/codegen/extract/messages.specs.ts +30 -30
- package/src/codegen/extract/messages.ts +16 -16
- package/src/codegen/extract/projection.specs.ts +22 -22
- package/src/codegen/extract/projection.ts +9 -9
- package/src/codegen/extract/query.ts +2 -2
- package/src/codegen/extract/slice-normalizer.specs.ts +11 -11
- package/src/codegen/extract/slice-normalizer.ts +14 -14
- package/src/codegen/extract/states.ts +4 -4
- package/src/codegen/extract/step-converter.specs.ts +9 -9
- package/src/codegen/extract/step-converter.ts +15 -15
- package/src/codegen/extract/step-types.specs.ts +12 -12
- package/src/codegen/extract/step-types.ts +4 -4
- package/src/codegen/findEventSource.specs.ts +23 -23
- package/src/codegen/scaffoldErrors.specs.ts +4 -4
- package/src/codegen/scaffoldFromSchema.filter.specs.ts +32 -32
- package/src/codegen/scaffoldFromSchema.ts +146 -132
- package/src/codegen/templates/command/commands.specs.ts +3 -3
- package/src/codegen/templates/command/decide.specs.specs.ts +52 -52
- package/src/codegen/templates/command/decide.specs.ts +12 -12
- package/src/codegen/templates/command/decide.specs.ts.ejs +1 -1
- package/src/codegen/templates/command/events.specs.ts +3 -3
- package/src/codegen/templates/command/evolve.specs.ts +3 -3
- package/src/codegen/templates/command/handle.specs.ts +13 -13
- package/src/codegen/templates/command/mutation.resolver.specs.ts +19 -19
- package/src/codegen/templates/command/register.specs.ts +3 -3
- package/src/codegen/templates/command/state.specs.ts +3 -3
- package/src/codegen/templates/query/events.specs.ts +4 -4
- package/src/codegen/templates/query/projection.specs.specs.ts +60 -60
- package/src/codegen/templates/query/projection.specs.ts +54 -29
- package/src/codegen/templates/query/projection.specs.ts.ejs +2 -2
- package/src/codegen/templates/query/query.resolver.specs.ts +63 -63
- package/src/codegen/templates/query/query.resolver.ts.ejs +1 -1
- package/src/codegen/templates/query/state.specs.ts +9 -9
- package/src/codegen/templates/react/react.specs.specs.ts +15 -15
- package/src/codegen/templates/react/react.specs.ts +16 -16
- package/src/codegen/templates/react/react.specs.ts.ejs +9 -9
- package/src/codegen/templates/react/react.ts.ejs +5 -5
- package/src/codegen/templates/react/react.ts.specs.ts +33 -33
- package/src/codegen/templates/react/register.specs.ts +7 -7
- package/src/codegen/templates/react/register.ts.ejs +4 -4
- package/src/codegen/test-data/specVariant1.json +1 -1
- package/src/codegen/test-data/specVariant1.ts +3 -2
- package/src/codegen/test-data/specVariant2.json +1 -1
- package/src/codegen/types.ts +2 -2
- package/src/commands/generate-server.specs.ts +81 -79
- package/src/commands/generate-server.ts +110 -88
- package/src/commands/initialize-server.specs.ts +4 -4
- package/src/commands/initialize-server.ts +5 -2
- package/src/domain/flows/shared/types.ts +15 -0
- package/src/domain/narratives/shared/types.ts +15 -0
- package/src/index.ts +1 -1
- package/src/server.ts +3 -3
- package/src/utils/loadRegisterFiles.ts +5 -5
- package/src/utils/loadResolvers.ts +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Scene } from '@auto-engineer/narrative';
|
|
2
2
|
import { describe, expect, it } from 'vitest';
|
|
3
3
|
import type { Message } from '../types';
|
|
4
4
|
import { buildEventIdFieldMap } from './projection';
|
|
@@ -10,10 +10,10 @@ describe('buildEventIdFieldMap', () => {
|
|
|
10
10
|
{ type: 'ExerciseLogged', fields: [] },
|
|
11
11
|
];
|
|
12
12
|
|
|
13
|
-
const
|
|
13
|
+
const scenes: Scene[] = [
|
|
14
14
|
{
|
|
15
|
-
name: 'workout-
|
|
16
|
-
|
|
15
|
+
name: 'workout-scene',
|
|
16
|
+
moments: [
|
|
17
17
|
{
|
|
18
18
|
type: 'command',
|
|
19
19
|
name: 'create-workout',
|
|
@@ -76,13 +76,13 @@ describe('buildEventIdFieldMap', () => {
|
|
|
76
76
|
},
|
|
77
77
|
];
|
|
78
78
|
|
|
79
|
-
const findEventSourceFn = (_fls:
|
|
80
|
-
if (eventType === 'WorkoutCreated') return {
|
|
81
|
-
if (eventType === 'ExerciseLogged') return {
|
|
79
|
+
const findEventSourceFn = (_fls: Scene[], eventType: string) => {
|
|
80
|
+
if (eventType === 'WorkoutCreated') return { sceneName: 'workout-scene', momentName: 'create-workout' };
|
|
81
|
+
if (eventType === 'ExerciseLogged') return { sceneName: 'workout-scene', momentName: 'log-exercises' };
|
|
82
82
|
return null;
|
|
83
83
|
};
|
|
84
84
|
|
|
85
|
-
const result = buildEventIdFieldMap(events,
|
|
85
|
+
const result = buildEventIdFieldMap(events, scenes, findEventSourceFn);
|
|
86
86
|
expect(result).toEqual(
|
|
87
87
|
new Map([
|
|
88
88
|
['WorkoutCreated', 'id'],
|
|
@@ -97,10 +97,10 @@ describe('buildEventIdFieldMap', () => {
|
|
|
97
97
|
{ type: 'WorkoutUpdated', fields: [] },
|
|
98
98
|
];
|
|
99
99
|
|
|
100
|
-
const
|
|
100
|
+
const scenes: Scene[] = [
|
|
101
101
|
{
|
|
102
|
-
name: 'workout-
|
|
103
|
-
|
|
102
|
+
name: 'workout-scene',
|
|
103
|
+
moments: [
|
|
104
104
|
{
|
|
105
105
|
type: 'command',
|
|
106
106
|
name: 'manage-workout',
|
|
@@ -141,12 +141,12 @@ describe('buildEventIdFieldMap', () => {
|
|
|
141
141
|
},
|
|
142
142
|
];
|
|
143
143
|
|
|
144
|
-
const findEventSourceFn = (_fls:
|
|
145
|
-
|
|
146
|
-
|
|
144
|
+
const findEventSourceFn = (_fls: Scene[], _eventType: string) => ({
|
|
145
|
+
sceneName: 'workout-scene',
|
|
146
|
+
momentName: 'manage-workout',
|
|
147
147
|
});
|
|
148
148
|
|
|
149
|
-
const result = buildEventIdFieldMap(events,
|
|
149
|
+
const result = buildEventIdFieldMap(events, scenes, findEventSourceFn);
|
|
150
150
|
expect(result).toEqual(
|
|
151
151
|
new Map([
|
|
152
152
|
['WorkoutCreated', 'id'],
|
|
@@ -157,20 +157,20 @@ describe('buildEventIdFieldMap', () => {
|
|
|
157
157
|
|
|
158
158
|
it('returns undefined when no producing slice is found', () => {
|
|
159
159
|
const events: Message[] = [{ type: 'UnknownEvent', fields: [] }];
|
|
160
|
-
const
|
|
160
|
+
const scenes: Scene[] = [];
|
|
161
161
|
const findEventSourceFn = () => null;
|
|
162
162
|
|
|
163
|
-
const result = buildEventIdFieldMap(events,
|
|
163
|
+
const result = buildEventIdFieldMap(events, scenes, findEventSourceFn);
|
|
164
164
|
expect(result).toEqual(undefined);
|
|
165
165
|
});
|
|
166
166
|
|
|
167
167
|
it('skips events from slices with composite keys', () => {
|
|
168
168
|
const events: Message[] = [{ type: 'UserJoined', fields: [] }];
|
|
169
169
|
|
|
170
|
-
const
|
|
170
|
+
const scenes: Scene[] = [
|
|
171
171
|
{
|
|
172
|
-
name: '
|
|
173
|
-
|
|
172
|
+
name: 'scene',
|
|
173
|
+
moments: [
|
|
174
174
|
{
|
|
175
175
|
type: 'command',
|
|
176
176
|
name: 'join',
|
|
@@ -204,9 +204,9 @@ describe('buildEventIdFieldMap', () => {
|
|
|
204
204
|
},
|
|
205
205
|
];
|
|
206
206
|
|
|
207
|
-
const findEventSourceFn = () => ({
|
|
207
|
+
const findEventSourceFn = () => ({ sceneName: 'scene', momentName: 'join' });
|
|
208
208
|
|
|
209
|
-
const result = buildEventIdFieldMap(events,
|
|
209
|
+
const result = buildEventIdFieldMap(events, scenes, findEventSourceFn);
|
|
210
210
|
expect(result).toEqual(undefined);
|
|
211
211
|
});
|
|
212
212
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Moment, Scene } from '@auto-engineer/narrative';
|
|
2
2
|
import type { Message } from '../types';
|
|
3
3
|
import { extractStreamIdFields } from './data-sink';
|
|
4
4
|
|
|
@@ -26,7 +26,7 @@ function isProjectionOrigin(origin: unknown): origin is ProjectionOrigin {
|
|
|
26
26
|
return obj.type === 'projection';
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
function extractProjectionField<K extends keyof ProjectionOrigin>(slice:
|
|
29
|
+
function extractProjectionField<K extends keyof ProjectionOrigin>(slice: Moment, fieldName: K): string | undefined {
|
|
30
30
|
if (!('server' in slice)) return undefined;
|
|
31
31
|
const dataSource = slice.server?.data?.items?.[0];
|
|
32
32
|
if (!hasOrigin(dataSource)) return undefined;
|
|
@@ -42,7 +42,7 @@ function extractProjectionField<K extends keyof ProjectionOrigin>(slice: Slice,
|
|
|
42
42
|
return undefined;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
export function extractProjectionIdField(slice:
|
|
45
|
+
export function extractProjectionIdField(slice: Moment): string | string[] | undefined {
|
|
46
46
|
if (!('server' in slice)) return undefined;
|
|
47
47
|
const dataSource = slice.server?.data?.items?.[0];
|
|
48
48
|
if (!hasOrigin(dataSource)) return undefined;
|
|
@@ -56,20 +56,20 @@ export function extractProjectionIdField(slice: Slice): string | string[] | unde
|
|
|
56
56
|
return undefined;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
export function extractProjectionName(slice:
|
|
59
|
+
export function extractProjectionName(slice: Moment): string | undefined {
|
|
60
60
|
return extractProjectionField(slice, 'name');
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
export function buildEventIdFieldMap(
|
|
64
64
|
events: Message[],
|
|
65
|
-
|
|
66
|
-
findEventSourceFn: (
|
|
65
|
+
scenes: Scene[],
|
|
66
|
+
findEventSourceFn: (scenes: Scene[], eventType: string) => { sceneName: string; momentName: string } | null,
|
|
67
67
|
): Map<string, string> | undefined {
|
|
68
68
|
const map = new Map<string, string>();
|
|
69
69
|
for (const event of events) {
|
|
70
|
-
const source = findEventSourceFn(
|
|
70
|
+
const source = findEventSourceFn(scenes, event.type);
|
|
71
71
|
if (!source) continue;
|
|
72
|
-
const slice =
|
|
72
|
+
const slice = scenes.find((f) => f.name === source.sceneName)?.moments.find((s) => s.name === source.momentName);
|
|
73
73
|
if (!slice || typeof slice.stream !== 'string') continue;
|
|
74
74
|
const fields = extractStreamIdFields(slice.stream);
|
|
75
75
|
if (fields.length === 1) {
|
|
@@ -79,7 +79,7 @@ export function buildEventIdFieldMap(
|
|
|
79
79
|
return map.size > 0 ? map : undefined;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
export function extractProjectionSingleton(slice:
|
|
82
|
+
export function extractProjectionSingleton(slice: Moment): boolean {
|
|
83
83
|
if (!('server' in slice)) return false;
|
|
84
84
|
const dataSource = slice.server?.data?.items?.[0];
|
|
85
85
|
if (!hasOrigin(dataSource)) return false;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MappingEntry,
|
|
1
|
+
import type { MappingEntry, Moment } from '@auto-engineer/narrative';
|
|
2
2
|
import type { EventRef, StateRef } from '../types';
|
|
3
3
|
import { extractGwtFromSpecs, isQueryAction, type QueryActionRef } from './step-converter';
|
|
4
4
|
|
|
@@ -16,7 +16,7 @@ export interface QueryGwtCondition {
|
|
|
16
16
|
then: Array<StateRef>;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export function buildQueryGwtMapping(slice:
|
|
19
|
+
export function buildQueryGwtMapping(slice: Moment): QueryGwtCondition[] {
|
|
20
20
|
if (slice.type !== 'query') return [];
|
|
21
21
|
|
|
22
22
|
const specs = slice.server?.specs;
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Moment, Spec } from '@auto-engineer/narrative';
|
|
2
2
|
import { describe, expect, it } from 'vitest';
|
|
3
3
|
import type { MessageDefinition } from '../types';
|
|
4
|
-
import {
|
|
4
|
+
import { normalizeMomentForTemplate } from './slice-normalizer';
|
|
5
5
|
|
|
6
|
-
function
|
|
6
|
+
function createReactMoment(specs: Spec[]): Moment {
|
|
7
7
|
return {
|
|
8
8
|
type: 'react',
|
|
9
9
|
name: 'NotifyUpcomingWorkout',
|
|
10
10
|
client: { specs: [] },
|
|
11
11
|
server: { description: '', specs },
|
|
12
|
-
} as unknown as
|
|
12
|
+
} as unknown as Moment;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
describe('
|
|
15
|
+
describe('moment-normalizer', () => {
|
|
16
16
|
describe('normalizeReactPatternB — typical pattern preserves command in then', () => {
|
|
17
17
|
it('should move trigger event to when and place original command in then', () => {
|
|
18
18
|
const specs: Spec[] = [
|
|
@@ -76,8 +76,8 @@ describe('slice-normalizer', () => {
|
|
|
76
76
|
},
|
|
77
77
|
];
|
|
78
78
|
|
|
79
|
-
const slice =
|
|
80
|
-
const result =
|
|
79
|
+
const slice = createReactMoment(specs);
|
|
80
|
+
const result = normalizeMomentForTemplate(slice, allMessages);
|
|
81
81
|
const normalizedSpecs = result.server?.specs;
|
|
82
82
|
const example = normalizedSpecs?.rules[0].examples[0];
|
|
83
83
|
|
|
@@ -136,8 +136,8 @@ describe('slice-normalizer', () => {
|
|
|
136
136
|
},
|
|
137
137
|
];
|
|
138
138
|
|
|
139
|
-
const slice =
|
|
140
|
-
const result =
|
|
139
|
+
const slice = createReactMoment(specs);
|
|
140
|
+
const result = normalizeMomentForTemplate(slice, allMessages);
|
|
141
141
|
const example = result.server?.specs?.rules[0].examples[0];
|
|
142
142
|
|
|
143
143
|
expect(example?.then).toEqual([{ commandRef: 'NotifyAchievementOpportunity', exampleData: { memberId: 'm1' } }]);
|
|
@@ -166,8 +166,8 @@ describe('slice-normalizer', () => {
|
|
|
166
166
|
},
|
|
167
167
|
];
|
|
168
168
|
|
|
169
|
-
const slice =
|
|
170
|
-
const result =
|
|
169
|
+
const slice = createReactMoment(specs);
|
|
170
|
+
const result = normalizeMomentForTemplate(slice, allMessages);
|
|
171
171
|
const example = result.server?.specs?.rules[0].examples[0];
|
|
172
172
|
|
|
173
173
|
expect(example?.then).toEqual([{ commandRef: 'NotifyAchievementOpportunity', exampleData: { memberId: 'm1' } }]);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Moment, Spec } from '@auto-engineer/narrative';
|
|
2
2
|
import type { MessageDefinition } from '../types';
|
|
3
3
|
import {
|
|
4
4
|
type CommandRef,
|
|
@@ -6,9 +6,9 @@ import {
|
|
|
6
6
|
type EventRef,
|
|
7
7
|
extractGwtFromSpecs,
|
|
8
8
|
type GwtConditionWithRule,
|
|
9
|
-
|
|
9
|
+
getMomentType,
|
|
10
|
+
type MomentType,
|
|
10
11
|
type QueryActionRef,
|
|
11
|
-
type SliceType,
|
|
12
12
|
type StateRef,
|
|
13
13
|
} from './step-converter';
|
|
14
14
|
|
|
@@ -96,12 +96,12 @@ function filterReactThenToCommands(rules: NormalizedRule[], allMessages: Message
|
|
|
96
96
|
|
|
97
97
|
function normalizeSpecsForTemplate(
|
|
98
98
|
specs: Spec[],
|
|
99
|
-
|
|
99
|
+
momentType: MomentType,
|
|
100
100
|
allMessages?: MessageDefinition[],
|
|
101
101
|
): NormalizedSpecs | null {
|
|
102
102
|
if (specs.length === 0) return null;
|
|
103
103
|
|
|
104
|
-
const gwtResults = extractGwtFromSpecs(specs,
|
|
104
|
+
const gwtResults = extractGwtFromSpecs(specs, momentType);
|
|
105
105
|
const groupedByRule = groupGwtByRule(gwtResults);
|
|
106
106
|
|
|
107
107
|
const featureName = specs[0]?.feature ?? '';
|
|
@@ -111,7 +111,7 @@ function normalizeSpecsForTemplate(
|
|
|
111
111
|
examples: gwts.map(gwtToNormalizedExample),
|
|
112
112
|
}));
|
|
113
113
|
|
|
114
|
-
if (
|
|
114
|
+
if (momentType === 'react' && allMessages) {
|
|
115
115
|
normalizeReactPatternB(rules, allMessages);
|
|
116
116
|
filterReactThenToCommands(rules, allMessages);
|
|
117
117
|
}
|
|
@@ -122,21 +122,21 @@ function normalizeSpecsForTemplate(
|
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
type
|
|
126
|
-
type
|
|
125
|
+
type MomentWithServer = Moment & { server?: { specs?: Spec[] } };
|
|
126
|
+
type NormalizedMoment<T extends MomentWithServer> = Omit<T, 'server'> & {
|
|
127
127
|
server?: Omit<T['server'], 'specs'> & { specs?: NormalizedSpecs | null };
|
|
128
128
|
};
|
|
129
129
|
|
|
130
|
-
export function
|
|
130
|
+
export function normalizeMomentForTemplate<T extends MomentWithServer>(
|
|
131
131
|
slice: T,
|
|
132
132
|
allMessages?: MessageDefinition[],
|
|
133
|
-
):
|
|
133
|
+
): NormalizedMoment<T> {
|
|
134
134
|
if (!('server' in slice) || !slice.server?.specs) {
|
|
135
|
-
return slice as
|
|
135
|
+
return slice as NormalizedMoment<T>;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
const
|
|
139
|
-
const normalizedSpecs = normalizeSpecsForTemplate(slice.server.specs,
|
|
138
|
+
const momentType = getMomentType(slice);
|
|
139
|
+
const normalizedSpecs = normalizeSpecsForTemplate(slice.server.specs, momentType, allMessages);
|
|
140
140
|
|
|
141
141
|
return {
|
|
142
142
|
...slice,
|
|
@@ -144,5 +144,5 @@ export function normalizeSliceForTemplate<T extends SliceWithServer>(
|
|
|
144
144
|
...slice.server,
|
|
145
145
|
specs: normalizedSpecs,
|
|
146
146
|
},
|
|
147
|
-
} as
|
|
147
|
+
} as NormalizedMoment<T>;
|
|
148
148
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Moment } from '@auto-engineer/narrative';
|
|
2
2
|
import type { EventRef, Message, MessageDefinition } from '../types';
|
|
3
3
|
import { extractFieldsFromMessage } from './fields';
|
|
4
4
|
|
|
@@ -16,7 +16,7 @@ function createStateMessage(stateName: string, allMessages: MessageDefinition[])
|
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
function hasServerData(slice:
|
|
19
|
+
function hasServerData(slice: Moment): slice is Moment & { server: { data: { items: unknown[] } } } {
|
|
20
20
|
return (
|
|
21
21
|
'server' in slice &&
|
|
22
22
|
Boolean(slice.server) &&
|
|
@@ -33,7 +33,7 @@ export function extractStatesFromGiven(givenRefs: EventRef[], allMessages: Messa
|
|
|
33
33
|
.map((ref) => createStateMessage(ref.eventRef, allMessages));
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export function extractStatesFromTarget(slice:
|
|
36
|
+
export function extractStatesFromTarget(slice: Moment, allMessages: MessageDefinition[]): Message[] {
|
|
37
37
|
if (!hasServerData(slice)) {
|
|
38
38
|
return [];
|
|
39
39
|
}
|
|
@@ -46,7 +46,7 @@ export function extractStatesFromTarget(slice: Slice, allMessages: MessageDefini
|
|
|
46
46
|
return uniqueTargets.map((name) => createStateMessage(name, allMessages));
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
export function extractStatesFromData(slice:
|
|
49
|
+
export function extractStatesFromData(slice: Moment, allMessages: MessageDefinition[]): Message[] {
|
|
50
50
|
if (!hasServerData(slice)) {
|
|
51
51
|
return [];
|
|
52
52
|
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import type { Example,
|
|
1
|
+
import type { Example, Moment } from '@auto-engineer/narrative';
|
|
2
2
|
import { describe, expect, it } from 'vitest';
|
|
3
3
|
import { isQueryAction, stepsToGwt } from './step-converter';
|
|
4
4
|
|
|
5
5
|
describe('step-converter', () => {
|
|
6
6
|
describe('stepsToGwt with query slices', () => {
|
|
7
|
-
const
|
|
7
|
+
const createQueryMoment = (request?: string): Moment =>
|
|
8
8
|
({
|
|
9
9
|
type: 'query',
|
|
10
10
|
name: 'View Workout Plan',
|
|
11
11
|
request,
|
|
12
12
|
client: { specs: [] },
|
|
13
13
|
server: { description: '', specs: [] },
|
|
14
|
-
}) as unknown as
|
|
14
|
+
}) as unknown as Moment;
|
|
15
15
|
|
|
16
16
|
const createExample = (
|
|
17
17
|
steps: Array<{ keyword: string; text: string; docString?: Record<string, unknown> }>,
|
|
@@ -27,7 +27,7 @@ describe('step-converter', () => {
|
|
|
27
27
|
|
|
28
28
|
describe('Pattern 2: Query Action', () => {
|
|
29
29
|
it('should produce QueryActionRef when When matches query name from request', () => {
|
|
30
|
-
const slice =
|
|
30
|
+
const slice = createQueryMoment('query ViewWorkoutPlan($workoutId: ID!) { workoutPlan { id } }');
|
|
31
31
|
const example = createExample([
|
|
32
32
|
{ keyword: 'Given', text: 'WorkoutPlanCreated', docString: { workoutId: 'wrk_123' } },
|
|
33
33
|
{ keyword: 'When', text: 'ViewWorkoutPlan', docString: { workoutId: 'wrk_123' } },
|
|
@@ -44,7 +44,7 @@ describe('step-converter', () => {
|
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
it('should produce QueryActionRef when When matches query name case-insensitively', () => {
|
|
47
|
-
const slice =
|
|
47
|
+
const slice = createQueryMoment('query viewWorkoutHistory { history { id } }');
|
|
48
48
|
const example = createExample([
|
|
49
49
|
{ keyword: 'Given', text: 'WorkoutPlanCreated', docString: { workoutId: 'wrk_123' } },
|
|
50
50
|
{ keyword: 'When', text: 'ViewWorkoutHistory', docString: { userId: 'user_456' } },
|
|
@@ -61,7 +61,7 @@ describe('step-converter', () => {
|
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
it('should produce EventRef[] when no request field is provided', () => {
|
|
64
|
-
const slice =
|
|
64
|
+
const slice = createQueryMoment(); // No request = cannot detect query action
|
|
65
65
|
const example = createExample([
|
|
66
66
|
{ keyword: 'Given', text: 'UserCreated', docString: { userId: 'user_123' } },
|
|
67
67
|
{ keyword: 'When', text: 'GetUserProfile', docString: { userId: 'user_123' } },
|
|
@@ -78,7 +78,7 @@ describe('step-converter', () => {
|
|
|
78
78
|
|
|
79
79
|
describe('Pattern 1: Event-based', () => {
|
|
80
80
|
it('should produce EventRef[] when When contains event names', () => {
|
|
81
|
-
const slice =
|
|
81
|
+
const slice = createQueryMoment('query ViewWorkoutPlan { workoutPlan { id } }');
|
|
82
82
|
const example = createExample([
|
|
83
83
|
{ keyword: 'Given', text: 'WorkoutPlanCreated', docString: { workoutId: 'wrk_123' } },
|
|
84
84
|
{ keyword: 'When', text: 'WorkoutPlanUpdated', docString: { workoutId: 'wrk_123', name: 'Updated Plan' } },
|
|
@@ -96,7 +96,7 @@ describe('step-converter', () => {
|
|
|
96
96
|
});
|
|
97
97
|
|
|
98
98
|
it('should produce EventRef[] when When does not match query action patterns', () => {
|
|
99
|
-
const slice =
|
|
99
|
+
const slice = createQueryMoment();
|
|
100
100
|
const example = createExample([
|
|
101
101
|
{ keyword: 'Given', text: 'OrderCreated', docString: { orderId: 'ord_123' } },
|
|
102
102
|
{ keyword: 'When', text: 'OrderShipped', docString: { orderId: 'ord_123' } },
|
|
@@ -110,7 +110,7 @@ describe('step-converter', () => {
|
|
|
110
110
|
});
|
|
111
111
|
|
|
112
112
|
it('should produce EventRef[] for multiple When events', () => {
|
|
113
|
-
const slice =
|
|
113
|
+
const slice = createQueryMoment();
|
|
114
114
|
const example = createExample([
|
|
115
115
|
{ keyword: 'Given', text: 'AccountCreated', docString: { accountId: 'acc_123' } },
|
|
116
116
|
{ keyword: 'When', text: 'DepositMade', docString: { amount: 100 } },
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import type { Example,
|
|
1
|
+
import type { Example, Moment, Spec } from '@auto-engineer/narrative';
|
|
2
2
|
import {
|
|
3
3
|
type CommandRef,
|
|
4
4
|
detectQueryAction,
|
|
5
5
|
type ErrorRef,
|
|
6
6
|
type EventRef,
|
|
7
|
-
|
|
7
|
+
getMomentType,
|
|
8
8
|
isQueryAction,
|
|
9
9
|
isStepWithDocString,
|
|
10
10
|
isStepWithError,
|
|
11
11
|
type MajorKeyword,
|
|
12
|
+
type MomentType,
|
|
12
13
|
type QueryActionRef,
|
|
13
14
|
resolveMajorKeyword,
|
|
14
|
-
type SliceType,
|
|
15
15
|
type StateRef,
|
|
16
16
|
} from './step-types';
|
|
17
17
|
|
|
18
|
-
export type { CommandRef, EventRef, StateRef, ErrorRef,
|
|
19
|
-
export {
|
|
18
|
+
export type { CommandRef, EventRef, StateRef, ErrorRef, MomentType, QueryActionRef };
|
|
19
|
+
export { getMomentType, isQueryAction };
|
|
20
20
|
|
|
21
21
|
export interface GwtResult {
|
|
22
22
|
given: EventRef[];
|
|
@@ -63,10 +63,10 @@ function parseSteps(example: Example): ParsedSteps {
|
|
|
63
63
|
return { given, whenItems, thenItems, thenErrors };
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
function buildGwtResult(
|
|
66
|
+
function buildGwtResult(momentType: MomentType, parsed: ParsedSteps, description: string, slice?: Moment): GwtResult {
|
|
67
67
|
const { given, whenItems, thenItems, thenErrors } = parsed;
|
|
68
68
|
|
|
69
|
-
if (
|
|
69
|
+
if (momentType === 'command') {
|
|
70
70
|
return {
|
|
71
71
|
given,
|
|
72
72
|
when: { commandRef: whenItems[0]?.text ?? '', exampleData: whenItems[0]?.exampleData ?? {} },
|
|
@@ -75,7 +75,7 @@ function buildGwtResult(sliceType: SliceType, parsed: ParsedSteps, description:
|
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
if (
|
|
78
|
+
if (momentType === 'react') {
|
|
79
79
|
return {
|
|
80
80
|
given,
|
|
81
81
|
when: whenItems.map((w) => ({ eventRef: w.text, exampleData: w.exampleData })),
|
|
@@ -84,7 +84,7 @@ function buildGwtResult(sliceType: SliceType, parsed: ParsedSteps, description:
|
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
if (
|
|
87
|
+
if (momentType === 'query' && slice && whenItems.length > 0) {
|
|
88
88
|
const firstWhenText = whenItems[0]?.text ?? '';
|
|
89
89
|
if (detectQueryAction(firstWhenText, slice)) {
|
|
90
90
|
return {
|
|
@@ -105,22 +105,22 @@ function buildGwtResult(sliceType: SliceType, parsed: ParsedSteps, description:
|
|
|
105
105
|
};
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
export function stepsToGwt(example: Example,
|
|
108
|
+
export function stepsToGwt(example: Example, momentType: MomentType, slice?: Moment): GwtResult {
|
|
109
109
|
const parsed = parseSteps(example);
|
|
110
|
-
return buildGwtResult(
|
|
110
|
+
return buildGwtResult(momentType, parsed, example.name, slice);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
export interface GwtConditionWithRule extends GwtResult {
|
|
114
114
|
ruleDescription: string;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
export function extractGwtFromSpecs(specs: Spec[],
|
|
117
|
+
export function extractGwtFromSpecs(specs: Spec[], momentType: MomentType, slice?: Moment): GwtConditionWithRule[] {
|
|
118
118
|
const results: GwtConditionWithRule[] = [];
|
|
119
119
|
|
|
120
120
|
for (const spec of specs) {
|
|
121
121
|
for (const rule of spec.rules) {
|
|
122
122
|
for (const example of rule.examples) {
|
|
123
|
-
const gwt = stepsToGwt(example,
|
|
123
|
+
const gwt = stepsToGwt(example, momentType, slice);
|
|
124
124
|
results.push({
|
|
125
125
|
...gwt,
|
|
126
126
|
ruleDescription: rule.name,
|
|
@@ -132,9 +132,9 @@ export function extractGwtFromSpecs(specs: Spec[], sliceType: SliceType, slice?:
|
|
|
132
132
|
return results;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
export function
|
|
135
|
+
export function extractGwtSpecsFromMoment(slice: Moment): GwtConditionWithRule[] {
|
|
136
136
|
if (!('server' in slice)) return [];
|
|
137
137
|
const specs = slice.server?.specs;
|
|
138
138
|
if (!Array.isArray(specs) || specs.length === 0) return [];
|
|
139
|
-
return extractGwtFromSpecs(specs,
|
|
139
|
+
return extractGwtFromSpecs(specs, getMomentType(slice), slice);
|
|
140
140
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Moment } from '@auto-engineer/narrative';
|
|
2
2
|
import { describe, expect, it } from 'vitest';
|
|
3
3
|
import { detectQueryAction, extractQueryNameFromRequest, isQueryAction, type QueryActionRef } from './step-types';
|
|
4
4
|
|
|
@@ -79,55 +79,55 @@ describe('step-types', () => {
|
|
|
79
79
|
});
|
|
80
80
|
|
|
81
81
|
describe('detectQueryAction', () => {
|
|
82
|
-
const
|
|
82
|
+
const createQueryMoment = (request?: string): Moment =>
|
|
83
83
|
({
|
|
84
84
|
type: 'query',
|
|
85
85
|
name: 'View Workout Plan',
|
|
86
86
|
request,
|
|
87
87
|
client: { specs: [] },
|
|
88
88
|
server: { description: '', specs: [] },
|
|
89
|
-
}) as unknown as
|
|
89
|
+
}) as unknown as Moment;
|
|
90
90
|
|
|
91
|
-
const
|
|
91
|
+
const createCommandMoment = (): Moment =>
|
|
92
92
|
({
|
|
93
93
|
type: 'command',
|
|
94
94
|
name: 'Create Workout',
|
|
95
95
|
client: { specs: [] },
|
|
96
96
|
server: { description: '', specs: [] },
|
|
97
|
-
}) as unknown as
|
|
97
|
+
}) as unknown as Moment;
|
|
98
98
|
|
|
99
99
|
it('should return true when whenText exactly matches query name from request', () => {
|
|
100
|
-
const slice =
|
|
100
|
+
const slice = createQueryMoment('query ViewWorkoutPlan($id: ID!) { workoutPlan { id } }');
|
|
101
101
|
expect(detectQueryAction('ViewWorkoutPlan', slice)).toBe(true);
|
|
102
102
|
});
|
|
103
103
|
|
|
104
104
|
it('should return true when whenText matches query name case-insensitively', () => {
|
|
105
|
-
const slice =
|
|
105
|
+
const slice = createQueryMoment('query viewWorkoutPlan { workoutPlan { id } }');
|
|
106
106
|
expect(detectQueryAction('ViewWorkoutPlan', slice)).toBe(true);
|
|
107
107
|
});
|
|
108
108
|
|
|
109
109
|
it('should return false for command slices', () => {
|
|
110
|
-
const slice =
|
|
110
|
+
const slice = createCommandMoment();
|
|
111
111
|
expect(detectQueryAction('ViewWorkoutPlan', slice)).toBe(false);
|
|
112
112
|
});
|
|
113
113
|
|
|
114
114
|
it('should return false when no request field is provided', () => {
|
|
115
|
-
const slice =
|
|
115
|
+
const slice = createQueryMoment(); // No request
|
|
116
116
|
expect(detectQueryAction('ViewWorkoutPlan', slice)).toBe(false);
|
|
117
117
|
});
|
|
118
118
|
|
|
119
119
|
it('should return false for event names that do not match query name', () => {
|
|
120
|
-
const slice =
|
|
120
|
+
const slice = createQueryMoment('query ViewWorkoutPlan { workoutPlan { id } }');
|
|
121
121
|
expect(detectQueryAction('WorkoutPlanCreated', slice)).toBe(false);
|
|
122
122
|
});
|
|
123
123
|
|
|
124
124
|
it('should return false for empty whenText', () => {
|
|
125
|
-
const slice =
|
|
125
|
+
const slice = createQueryMoment('query ViewWorkoutPlan { workoutPlan { id } }');
|
|
126
126
|
expect(detectQueryAction('', slice)).toBe(false);
|
|
127
127
|
});
|
|
128
128
|
|
|
129
129
|
it('should return false when request contains mutation instead of query', () => {
|
|
130
|
-
const slice =
|
|
130
|
+
const slice = createQueryMoment('mutation CreateWorkout { createWorkout { id } }');
|
|
131
131
|
expect(detectQueryAction('CreateWorkout', slice)).toBe(false);
|
|
132
132
|
});
|
|
133
133
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Moment, Step, StepWithDocStringSchema, StepWithErrorSchema } from '@auto-engineer/narrative';
|
|
2
2
|
import {
|
|
3
3
|
detectQueryAction as detectQueryActionFromNarrative,
|
|
4
4
|
extractQueryNameFromRequest,
|
|
@@ -13,9 +13,9 @@ export type MajorKeyword = Exclude<StepKeyword, 'And'>;
|
|
|
13
13
|
|
|
14
14
|
export type ErrorType = StepWithError['error']['type'];
|
|
15
15
|
|
|
16
|
-
export type
|
|
16
|
+
export type MomentType = Extract<Moment['type'], 'command' | 'query' | 'react'>;
|
|
17
17
|
|
|
18
|
-
export function
|
|
18
|
+
export function getMomentType(slice: Moment): MomentType {
|
|
19
19
|
if (slice.type === 'command' || slice.type === 'query' || slice.type === 'react') {
|
|
20
20
|
return slice.type;
|
|
21
21
|
}
|
|
@@ -74,6 +74,6 @@ export { extractQueryNameFromRequest };
|
|
|
74
74
|
* Detects if the "When" text in a query slice represents a query action (query name)
|
|
75
75
|
* rather than an event name. Delegates to the narrative package implementation.
|
|
76
76
|
*/
|
|
77
|
-
export function detectQueryAction(whenText: string, slice:
|
|
77
|
+
export function detectQueryAction(whenText: string, slice: Moment): boolean {
|
|
78
78
|
return detectQueryActionFromNarrative(whenText, slice);
|
|
79
79
|
}
|