@auto-engineer/server-generator-apollo-emmett 1.147.0 → 1.149.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 +6 -6
- package/.turbo/turbo-type-check.log +1 -1
- package/CHANGELOG.md +53 -0
- package/README.md +174 -103
- package/dist/src/codegen/scaffoldFromSchema.d.ts.map +1 -1
- package/dist/src/codegen/scaffoldFromSchema.js +4 -0
- package/dist/src/codegen/scaffoldFromSchema.js.map +1 -1
- package/dist/src/codegen/templateHelpers.d.ts +11 -0
- package/dist/src/codegen/templateHelpers.d.ts.map +1 -0
- package/dist/src/codegen/templateHelpers.js +47 -0
- package/dist/src/codegen/templateHelpers.js.map +1 -0
- package/dist/src/codegen/templates/command/decide.specs.specs.ts +473 -0
- package/dist/src/codegen/templates/command/decide.specs.ts +1 -10
- package/dist/src/codegen/templates/command/decide.specs.ts.ejs +0 -45
- package/dist/src/codegen/templates/command/decide.ts.ejs +31 -2
- package/dist/src/codegen/templates/command/state.specs.ts +89 -0
- package/dist/src/codegen/templates/command/state.ts.ejs +20 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/ketchup-plan.md +5 -3
- package/package.json +4 -4
- package/src/codegen/scaffoldFromSchema.ts +4 -0
- package/src/codegen/templateHelpers.specs.ts +98 -0
- package/src/codegen/templateHelpers.ts +58 -0
- package/src/codegen/templates/command/decide.specs.specs.ts +473 -0
- package/src/codegen/templates/command/decide.specs.ts +1 -10
- package/src/codegen/templates/command/decide.specs.ts.ejs +0 -45
- package/src/codegen/templates/command/decide.ts.ejs +31 -2
- package/src/codegen/templates/command/state.specs.ts +89 -0
- package/src/codegen/templates/command/state.ts.ejs +20 -0
package/ketchup-plan.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
# Ketchup Plan: Fix
|
|
1
|
+
# Ketchup Plan: G4 — Fix remaining decide.ts implementer failures
|
|
2
2
|
|
|
3
3
|
## TODO
|
|
4
4
|
|
|
5
5
|
## DONE
|
|
6
6
|
|
|
7
|
-
- [x]
|
|
8
|
-
- [x]
|
|
7
|
+
- [x] B1: Extract shared template helpers and wire into template data [depends: none] (78f159a4)
|
|
8
|
+
- [x] B3: Add hasGivenStates + state context instruction to decide.ts.ejs [depends: B1] (42f66c71)
|
|
9
|
+
- [x] B4: Add context-aware nonCommandField classification to decide.ts.ejs [depends: B3] (65212e33)
|
|
10
|
+
- [x] B5: Add Given state ref hints to state.ts.ejs [depends: none]
|
package/package.json
CHANGED
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"uuid": "^13.0.0",
|
|
33
33
|
"web-streams-polyfill": "^4.1.0",
|
|
34
34
|
"zod": "^3.22.4",
|
|
35
|
-
"@auto-engineer/
|
|
36
|
-
"@auto-engineer/
|
|
35
|
+
"@auto-engineer/narrative": "1.149.0",
|
|
36
|
+
"@auto-engineer/message-bus": "1.149.0"
|
|
37
37
|
},
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public"
|
|
@@ -44,9 +44,9 @@
|
|
|
44
44
|
"typescript": "^5.8.3",
|
|
45
45
|
"vitest": "^3.2.4",
|
|
46
46
|
"tsx": "^4.19.2",
|
|
47
|
-
"@auto-engineer/cli": "1.
|
|
47
|
+
"@auto-engineer/cli": "1.149.0"
|
|
48
48
|
},
|
|
49
|
-
"version": "1.
|
|
49
|
+
"version": "1.149.0",
|
|
50
50
|
"scripts": {
|
|
51
51
|
"generate:server": "tsx src/cli/index.ts",
|
|
52
52
|
"build": "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",
|
|
@@ -47,6 +47,7 @@ import { buildEventIdFieldMap } from './extract/projection';
|
|
|
47
47
|
import { buildArgToStateFieldMap } from './extract/query';
|
|
48
48
|
import { normalizeMomentForTemplate } from './extract/slice-normalizer';
|
|
49
49
|
import { extractGwtSpecsFromMoment, type GwtResult } from './extract/step-converter';
|
|
50
|
+
import { buildKeepFieldNames, findDerivedDateInfo, isKeyTraceable } from './templateHelpers';
|
|
50
51
|
import type { GwtCondition, Message, MessageDefinition } from './types';
|
|
51
52
|
|
|
52
53
|
export class TemplateRenderError extends Error {
|
|
@@ -431,6 +432,9 @@ async function renderTemplate(
|
|
|
431
432
|
isReferencedMessageTypeArray,
|
|
432
433
|
extractReferencedTypeName,
|
|
433
434
|
referencedTypes: data.referencedTypes,
|
|
435
|
+
findDerivedDateInfo,
|
|
436
|
+
isKeyTraceable,
|
|
437
|
+
buildKeepFieldNames,
|
|
434
438
|
});
|
|
435
439
|
|
|
436
440
|
debugTemplate('Template rendered, output size: %d bytes', result.length);
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { buildKeepFieldNames, findDerivedDateInfo, isKeyTraceable } from './templateHelpers';
|
|
3
|
+
|
|
4
|
+
describe('findDerivedDateInfo', () => {
|
|
5
|
+
it('should detect a date field not in command fields and not in given events', () => {
|
|
6
|
+
const eventResults = [{ exampleData: { workoutId: 'w1', date: '2024-01-01', duration: 30 } }];
|
|
7
|
+
const commandFieldNames = new Set(['workoutId', 'duration']);
|
|
8
|
+
|
|
9
|
+
const result = findDerivedDateInfo(eventResults, commandFieldNames, undefined);
|
|
10
|
+
|
|
11
|
+
expect(result).toEqual({ date: '2024-01-01', fields: ['date'] });
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should return null date when no date-like fields exist', () => {
|
|
15
|
+
const eventResults = [{ exampleData: { workoutId: 'w1', duration: 30 } }];
|
|
16
|
+
const commandFieldNames = new Set(['workoutId', 'duration']);
|
|
17
|
+
|
|
18
|
+
const result = findDerivedDateInfo(eventResults, commandFieldNames, undefined);
|
|
19
|
+
|
|
20
|
+
expect(result).toEqual({ date: null, fields: [] });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should exclude date values that appear in given events', () => {
|
|
24
|
+
const eventResults = [{ exampleData: { id: 'x', startDate: '2024-01-01' } }];
|
|
25
|
+
const commandFieldNames = new Set(['id']);
|
|
26
|
+
const givenEvents = [{ exampleData: { startDate: '2024-01-01' } }];
|
|
27
|
+
|
|
28
|
+
const result = findDerivedDateInfo(eventResults, commandFieldNames, givenEvents);
|
|
29
|
+
|
|
30
|
+
expect(result).toEqual({ date: null, fields: [] });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should return null when multiple distinct dates exist', () => {
|
|
34
|
+
const eventResults = [{ exampleData: { startDate: '2024-01-01', endDate: '2024-02-01' } }];
|
|
35
|
+
const commandFieldNames = new Set<string>();
|
|
36
|
+
|
|
37
|
+
const result = findDerivedDateInfo(eventResults, commandFieldNames, undefined);
|
|
38
|
+
|
|
39
|
+
expect(result).toEqual({ date: null, fields: [] });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should group multiple fields sharing the same date', () => {
|
|
43
|
+
const eventResults = [{ exampleData: { createdAt: '2024-01-01', updatedAt: '2024-01-01' } }];
|
|
44
|
+
const commandFieldNames = new Set<string>();
|
|
45
|
+
|
|
46
|
+
const result = findDerivedDateInfo(eventResults, commandFieldNames, undefined);
|
|
47
|
+
|
|
48
|
+
expect(result).toEqual({ date: '2024-01-01', fields: ['createdAt', 'updatedAt'] });
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('isKeyTraceable', () => {
|
|
53
|
+
it('should return true when key+value match a given event', () => {
|
|
54
|
+
const givenEvents = [{ exampleData: { userId: 'u1' } }];
|
|
55
|
+
|
|
56
|
+
expect(isKeyTraceable('userId', 'u1', givenEvents)).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should return false when value does not match', () => {
|
|
60
|
+
const givenEvents = [{ exampleData: { userId: 'u1' } }];
|
|
61
|
+
|
|
62
|
+
expect(isKeyTraceable('userId', 'u2', givenEvents)).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should return false for null/undefined/object values', () => {
|
|
66
|
+
expect(isKeyTraceable('x', null, [{ exampleData: { x: null } }])).toBe(false);
|
|
67
|
+
expect(isKeyTraceable('x', undefined, [{ exampleData: { x: undefined } }])).toBe(false);
|
|
68
|
+
expect(isKeyTraceable('x', { a: 1 }, [{ exampleData: { x: { a: 1 } } }])).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should return false when no given events', () => {
|
|
72
|
+
expect(isKeyTraceable('x', 'v', undefined)).toBe(false);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('buildKeepFieldNames', () => {
|
|
77
|
+
it('should include command fields, derived date fields, and traceable fields', () => {
|
|
78
|
+
const eventResults = [{ exampleData: { workoutId: 'w1', userId: 'u1', date: '2024-01-01', duration: 30 } }];
|
|
79
|
+
const commandFieldNames = new Set(['workoutId', 'duration']);
|
|
80
|
+
const derivedDateFieldNames = ['date'];
|
|
81
|
+
const givenEvents = [{ exampleData: { userId: 'u1' } }];
|
|
82
|
+
|
|
83
|
+
const result = buildKeepFieldNames(eventResults, commandFieldNames, derivedDateFieldNames, givenEvents);
|
|
84
|
+
|
|
85
|
+
expect(result).toEqual(new Set(['workoutId', 'duration', 'date', 'userId']));
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should not include untraceable fields', () => {
|
|
89
|
+
const eventResults = [{ exampleData: { id: 'x', mystery: 'abc' } }];
|
|
90
|
+
const commandFieldNames = new Set(['id']);
|
|
91
|
+
const derivedDateFieldNames: string[] = [];
|
|
92
|
+
const givenEvents: Array<{ exampleData?: Record<string, unknown> }> = [];
|
|
93
|
+
|
|
94
|
+
const result = buildKeepFieldNames(eventResults, commandFieldNames, derivedDateFieldNames, givenEvents);
|
|
95
|
+
|
|
96
|
+
expect(result).toEqual(new Set(['id']));
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
interface EventData {
|
|
2
|
+
exampleData?: Record<string, unknown>;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function findDerivedDateInfo(
|
|
6
|
+
eventResults: EventData[],
|
|
7
|
+
commandFieldNames: Set<string>,
|
|
8
|
+
givenEvents: EventData[] | undefined,
|
|
9
|
+
): { date: string | null; fields: string[] } {
|
|
10
|
+
const givenValues = new Set<string>();
|
|
11
|
+
for (const g of givenEvents ?? []) {
|
|
12
|
+
for (const val of Object.values(g.exampleData ?? {})) {
|
|
13
|
+
if (typeof val === 'string') givenValues.add(val);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const fieldsByDate = new Map<string, string[]>();
|
|
17
|
+
for (const e of eventResults) {
|
|
18
|
+
for (const [key, val] of Object.entries(e.exampleData ?? {})) {
|
|
19
|
+
if (
|
|
20
|
+
!commandFieldNames.has(key) &&
|
|
21
|
+
typeof val === 'string' &&
|
|
22
|
+
/^\d{4}-\d{2}-\d{2}$/.test(val) &&
|
|
23
|
+
!givenValues.has(val)
|
|
24
|
+
) {
|
|
25
|
+
if (!fieldsByDate.has(val)) fieldsByDate.set(val, []);
|
|
26
|
+
fieldsByDate.get(val)!.push(key);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (fieldsByDate.size !== 1) return { date: null, fields: [] };
|
|
31
|
+
const [date, fields] = [...fieldsByDate.entries()][0];
|
|
32
|
+
return { date, fields };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function isKeyTraceable(key: string, value: unknown, givenEvents: EventData[] | undefined): boolean {
|
|
36
|
+
if (value === null || value === undefined || typeof value === 'object') return false;
|
|
37
|
+
for (const g of givenEvents ?? []) {
|
|
38
|
+
if ((g.exampleData ?? {})[key] === value) return true;
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function buildKeepFieldNames(
|
|
44
|
+
eventResults: EventData[],
|
|
45
|
+
commandFieldNames: Set<string>,
|
|
46
|
+
derivedDateFieldNames: string[],
|
|
47
|
+
givenEvents: EventData[] | undefined,
|
|
48
|
+
): Set<string> {
|
|
49
|
+
const keep = new Set([...commandFieldNames, ...derivedDateFieldNames]);
|
|
50
|
+
for (const e of eventResults) {
|
|
51
|
+
for (const [key, value] of Object.entries(e.exampleData ?? {})) {
|
|
52
|
+
if (!keep.has(key) && isKeyTraceable(key, value, givenEvents)) {
|
|
53
|
+
keep.add(key);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return keep;
|
|
58
|
+
}
|