@auto-engineer/server-generator-apollo-emmett 1.45.1 → 1.45.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 -5
- package/.turbo/turbo-type-check.log +1 -1
- package/CHANGELOG.md +23 -0
- package/dist/src/codegen/scaffoldFromSchema.js +2 -2
- package/dist/src/codegen/scaffoldFromSchema.js.map +1 -1
- package/dist/src/codegen/templates/command/mutation.resolver.specs.ts +1 -1
- package/dist/src/codegen/templates/query/projection.specs.ts +132 -0
- package/dist/src/codegen/templates/query/projection.ts.ejs +2 -2
- package/dist/src/codegen/templates/query/query.resolver.specs.ts +62 -0
- package/dist/src/codegen/templates/query/query.resolver.ts.ejs +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/ketchup-plan.md +2 -12
- package/package.json +4 -4
- package/src/codegen/scaffoldFromSchema.ts +2 -2
- package/src/codegen/templates/command/mutation.resolver.specs.ts +1 -1
- package/src/codegen/templates/query/projection.specs.ts +132 -0
- package/src/codegen/templates/query/projection.ts.ejs +2 -2
- package/src/codegen/templates/query/query.resolver.specs.ts +62 -0
- package/src/codegen/templates/query/query.resolver.ts.ejs +1 -1
package/ketchup-plan.md
CHANGED
|
@@ -1,17 +1,7 @@
|
|
|
1
|
-
# Ketchup Plan:
|
|
1
|
+
# Ketchup Plan: Fix `@Field(() => JSON)` uses JS built-in instead of GraphQL scalar
|
|
2
2
|
|
|
3
3
|
## TODO
|
|
4
4
|
|
|
5
5
|
## DONE
|
|
6
6
|
|
|
7
|
-
- [x] Burst 1:
|
|
8
|
-
- [x] Burst 2: InitializeServer creates all files in empty dir + emits ServerInitialized (cc8fe3c0)
|
|
9
|
-
- [x] Burst 3: InitializeServer is idempotent (doesn't overwrite existing files) (e8137a58)
|
|
10
|
-
- [x] Burst 4: InitializeServer emits ServerInitializationFailed on error (2f320556)
|
|
11
|
-
- [x] Burst 5: Update index.ts exports + auto.config.ts pipeline wiring (849fa106)
|
|
12
|
-
- [x] Burst 6: GenerateServer preserves health resolver after clean (8d37de62)
|
|
13
|
-
- [x] Burst 1: Command slices — Given-step state refs get local event type definitions (261a0d41)
|
|
14
|
-
- [x] Burst 2: findEventSource — check react data target events (9ac929f9)
|
|
15
|
-
- [x] Burst 3: extractMessagesForReact — extract data target events with source 'then' (f7aee89b)
|
|
16
|
-
- [x] Burst 4: Add events.ts.ejs template for react slices + defaultFilesByType entry (d2e5e5b8)
|
|
17
|
-
- [x] Burst 5: formatSpecValue — handle stringified JSON arrays (6cb406c3)
|
|
7
|
+
- [x] Burst 1: Fix graphqlType() to return 'GraphQLJSON' for object/inline-object types and update snapshots (c810efdd)
|
package/package.json
CHANGED
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"uuid": "^11.0.0",
|
|
33
33
|
"web-streams-polyfill": "^4.1.0",
|
|
34
34
|
"zod": "^3.22.4",
|
|
35
|
-
"@auto-engineer/narrative": "1.45.
|
|
36
|
-
"@auto-engineer/message-bus": "1.45.
|
|
35
|
+
"@auto-engineer/narrative": "1.45.2",
|
|
36
|
+
"@auto-engineer/message-bus": "1.45.2"
|
|
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.45.
|
|
47
|
+
"@auto-engineer/cli": "1.45.2"
|
|
48
48
|
},
|
|
49
|
-
"version": "1.45.
|
|
49
|
+
"version": "1.45.2",
|
|
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",
|
|
@@ -311,8 +311,8 @@ async function renderTemplate(
|
|
|
311
311
|
if (arr2 !== null) return `[${graphqlType(arr2[1].trim())}]`;
|
|
312
312
|
|
|
313
313
|
if (base === 'unknown' || base === 'any') return 'GraphQLJSON';
|
|
314
|
-
if (base === 'object') return '
|
|
315
|
-
if (isInlineObject(base)) return '
|
|
314
|
+
if (base === 'object') return 'GraphQLJSON';
|
|
315
|
+
if (isInlineObject(base)) return 'GraphQLJSON';
|
|
316
316
|
if (isStringLiteralUnion(base)) return resolveEnumOrString(base);
|
|
317
317
|
|
|
318
318
|
return convertPrimitiveType(base);
|
|
@@ -831,4 +831,136 @@ describe('projection.ts.ejs', () => {
|
|
|
831
831
|
"
|
|
832
832
|
`);
|
|
833
833
|
});
|
|
834
|
+
|
|
835
|
+
it('should not treat mixed union type with quotes as enum import', async () => {
|
|
836
|
+
const flows: Model = {
|
|
837
|
+
variant: 'specs',
|
|
838
|
+
narratives: [
|
|
839
|
+
{
|
|
840
|
+
name: 'order-flow',
|
|
841
|
+
slices: [
|
|
842
|
+
{
|
|
843
|
+
type: 'command',
|
|
844
|
+
name: 'place-order',
|
|
845
|
+
stream: 'order-${orderId}',
|
|
846
|
+
client: { specs: [] },
|
|
847
|
+
server: {
|
|
848
|
+
description: 'handles order placement',
|
|
849
|
+
specs: [
|
|
850
|
+
{
|
|
851
|
+
type: 'gherkin',
|
|
852
|
+
feature: 'Place order',
|
|
853
|
+
rules: [
|
|
854
|
+
{
|
|
855
|
+
name: 'Should handle order placement',
|
|
856
|
+
examples: [
|
|
857
|
+
{
|
|
858
|
+
name: 'User places order',
|
|
859
|
+
steps: [
|
|
860
|
+
{
|
|
861
|
+
keyword: 'When',
|
|
862
|
+
text: 'PlaceOrder',
|
|
863
|
+
docString: { orderId: 'order_1', total: 100 },
|
|
864
|
+
},
|
|
865
|
+
{
|
|
866
|
+
keyword: 'Then',
|
|
867
|
+
text: 'OrderPlaced',
|
|
868
|
+
docString: { orderId: 'order_1', total: 100 },
|
|
869
|
+
},
|
|
870
|
+
],
|
|
871
|
+
},
|
|
872
|
+
],
|
|
873
|
+
},
|
|
874
|
+
],
|
|
875
|
+
},
|
|
876
|
+
],
|
|
877
|
+
},
|
|
878
|
+
},
|
|
879
|
+
{
|
|
880
|
+
type: 'query',
|
|
881
|
+
name: 'view-order-status',
|
|
882
|
+
stream: 'orders',
|
|
883
|
+
client: { specs: [] },
|
|
884
|
+
server: {
|
|
885
|
+
description: 'projection for order status',
|
|
886
|
+
data: {
|
|
887
|
+
items: [
|
|
888
|
+
{
|
|
889
|
+
target: { type: 'State', name: 'OrderStatus' },
|
|
890
|
+
origin: {
|
|
891
|
+
type: 'projection',
|
|
892
|
+
name: 'OrderStatusProjection',
|
|
893
|
+
idField: 'orderId',
|
|
894
|
+
},
|
|
895
|
+
},
|
|
896
|
+
],
|
|
897
|
+
},
|
|
898
|
+
specs: [
|
|
899
|
+
{
|
|
900
|
+
type: 'gherkin',
|
|
901
|
+
feature: 'View order status',
|
|
902
|
+
rules: [
|
|
903
|
+
{
|
|
904
|
+
name: 'Should project order status',
|
|
905
|
+
examples: [
|
|
906
|
+
{
|
|
907
|
+
name: 'Order placed shows in status',
|
|
908
|
+
steps: [
|
|
909
|
+
{
|
|
910
|
+
keyword: 'When',
|
|
911
|
+
text: 'OrderPlaced',
|
|
912
|
+
docString: { orderId: 'order_1', total: 100 },
|
|
913
|
+
},
|
|
914
|
+
{
|
|
915
|
+
keyword: 'Then',
|
|
916
|
+
text: 'OrderStatus',
|
|
917
|
+
docString: { orderId: 'order_1', mixedStatus: 0 },
|
|
918
|
+
},
|
|
919
|
+
],
|
|
920
|
+
},
|
|
921
|
+
],
|
|
922
|
+
},
|
|
923
|
+
],
|
|
924
|
+
},
|
|
925
|
+
],
|
|
926
|
+
},
|
|
927
|
+
},
|
|
928
|
+
],
|
|
929
|
+
},
|
|
930
|
+
],
|
|
931
|
+
messages: [
|
|
932
|
+
{
|
|
933
|
+
type: 'command',
|
|
934
|
+
name: 'PlaceOrder',
|
|
935
|
+
fields: [
|
|
936
|
+
{ name: 'orderId', type: 'string', required: true },
|
|
937
|
+
{ name: 'total', type: 'number', required: true },
|
|
938
|
+
],
|
|
939
|
+
},
|
|
940
|
+
{
|
|
941
|
+
type: 'event',
|
|
942
|
+
name: 'OrderPlaced',
|
|
943
|
+
source: 'internal',
|
|
944
|
+
fields: [
|
|
945
|
+
{ name: 'orderId', type: 'string', required: true },
|
|
946
|
+
{ name: 'total', type: 'number', required: true },
|
|
947
|
+
],
|
|
948
|
+
},
|
|
949
|
+
{
|
|
950
|
+
type: 'state',
|
|
951
|
+
name: 'OrderStatus',
|
|
952
|
+
fields: [
|
|
953
|
+
{ name: 'orderId', type: 'string', required: true },
|
|
954
|
+
{ name: 'mixedStatus', type: 'number | "pending"', required: true },
|
|
955
|
+
],
|
|
956
|
+
},
|
|
957
|
+
],
|
|
958
|
+
};
|
|
959
|
+
|
|
960
|
+
const plans = await generateScaffoldFilePlans(flows.narratives, flows.messages, undefined, 'src/domain/flows');
|
|
961
|
+
const projectionFile = plans.find((p) => p.outputPath.endsWith('view-order-status/projection.ts'));
|
|
962
|
+
|
|
963
|
+
expect(projectionFile?.contents).not.toContain('number | "pending"');
|
|
964
|
+
expect(projectionFile?.contents).toContain('mixedStatus: /* TODO: map from event.data */ undefined as any');
|
|
965
|
+
});
|
|
834
966
|
});
|
|
@@ -26,7 +26,7 @@ if (targetName && messages) {
|
|
|
26
26
|
if (targetDef?.fields) {
|
|
27
27
|
for (const field of targetDef.fields) {
|
|
28
28
|
const fieldType = (field.type || '').replace(/\s*\|\s*null/g, '').trim();
|
|
29
|
-
if (
|
|
29
|
+
if (isEnumType(fieldType)) {
|
|
30
30
|
const enumName = toTsFieldType(field.type);
|
|
31
31
|
if (enumName && enumName !== 'String' && !stateEnums.includes(enumName)) {
|
|
32
32
|
stateEnums.push(enumName);
|
|
@@ -223,7 +223,7 @@ case '<%= event.type %>': {
|
|
|
223
223
|
placeholder = isNullable ? '/* TODO: map from event.data */ null' : '/* TODO: map from event.data */ new Date()';
|
|
224
224
|
} else if (baseType.startsWith('Array<')) {
|
|
225
225
|
placeholder = isNullable ? '/* TODO: map from event.data */ null' : '/* TODO: map from event.data */ []';
|
|
226
|
-
} else if (
|
|
226
|
+
} else if (isEnumType(baseType)) {
|
|
227
227
|
const enumName = toTsFieldType(type);
|
|
228
228
|
const firstValue = baseType.match(/['"]([^'"]+)['"]/)?.[1] ?? '';
|
|
229
229
|
const enumConstant = firstValue ? firstValue.toUpperCase().replace(/[^A-Z0-9]/g, '_') : '';
|
|
@@ -703,6 +703,67 @@ describe('query.resolver.ts.ejs', () => {
|
|
|
703
703
|
expect(resolverFile?.contents).not.toContain(', ReadModel');
|
|
704
704
|
});
|
|
705
705
|
|
|
706
|
+
it('should not treat mixed union type with quotes as enum in singleton default values', async () => {
|
|
707
|
+
const spec: SpecsSchema = {
|
|
708
|
+
variant: 'specs',
|
|
709
|
+
narratives: [
|
|
710
|
+
{
|
|
711
|
+
name: 'order-flow',
|
|
712
|
+
slices: [
|
|
713
|
+
{
|
|
714
|
+
type: 'query',
|
|
715
|
+
name: 'views-order-summary',
|
|
716
|
+
request: `
|
|
717
|
+
query GetOrderSummary {
|
|
718
|
+
orderSummary {
|
|
719
|
+
totalOrders
|
|
720
|
+
mixedStatus
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
`,
|
|
724
|
+
client: { specs: [] },
|
|
725
|
+
server: {
|
|
726
|
+
description: '',
|
|
727
|
+
data: {
|
|
728
|
+
items: [
|
|
729
|
+
{
|
|
730
|
+
origin: {
|
|
731
|
+
type: 'projection',
|
|
732
|
+
name: 'OrderSummaryProjection',
|
|
733
|
+
singleton: true,
|
|
734
|
+
},
|
|
735
|
+
target: {
|
|
736
|
+
type: 'State',
|
|
737
|
+
name: 'OrderSummary',
|
|
738
|
+
},
|
|
739
|
+
},
|
|
740
|
+
],
|
|
741
|
+
},
|
|
742
|
+
specs: [],
|
|
743
|
+
},
|
|
744
|
+
},
|
|
745
|
+
],
|
|
746
|
+
},
|
|
747
|
+
],
|
|
748
|
+
messages: [
|
|
749
|
+
{
|
|
750
|
+
type: 'state',
|
|
751
|
+
name: 'OrderSummary',
|
|
752
|
+
fields: [
|
|
753
|
+
{ name: 'totalOrders', type: 'number', required: true },
|
|
754
|
+
{ name: 'mixedStatus', type: 'number | "pending"', required: true },
|
|
755
|
+
],
|
|
756
|
+
},
|
|
757
|
+
],
|
|
758
|
+
};
|
|
759
|
+
|
|
760
|
+
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
761
|
+
const resolverFile = plans.find((p) => p.outputPath.endsWith('query.resolver.ts'));
|
|
762
|
+
|
|
763
|
+
expect(resolverFile?.contents).not.toContain("mixedStatus: 'pending'");
|
|
764
|
+
expect(resolverFile?.contents).toContain('mixedStatus: 0');
|
|
765
|
+
});
|
|
766
|
+
|
|
706
767
|
it('should handle nested Array<{...}> in embedded ObjectType without breaking', async () => {
|
|
707
768
|
const spec: SpecsSchema = {
|
|
708
769
|
variant: 'specs',
|
|
@@ -770,5 +831,6 @@ describe('query.resolver.ts.ejs', () => {
|
|
|
770
831
|
expect(resolverFile?.contents).toContain('export class WorkoutSessionPerformance');
|
|
771
832
|
expect(resolverFile?.contents).toContain('exerciseId!: string;');
|
|
772
833
|
expect(resolverFile?.contents).toContain('sets!: { reps: number; weight: number }[];');
|
|
834
|
+
expect(resolverFile?.contents).toContain('@Field(() => [GraphQLJSON])');
|
|
773
835
|
});
|
|
774
836
|
});
|
|
@@ -112,7 +112,7 @@ async <%= queryName %>(
|
|
|
112
112
|
defaultValue = 'new Date()';
|
|
113
113
|
} else if (baseType.startsWith('Array<')) {
|
|
114
114
|
defaultValue = '[]';
|
|
115
|
-
} else if (
|
|
115
|
+
} else if (isEnumType(baseType)) {
|
|
116
116
|
const firstValue = baseType.match(/['"]([^'"]+)['"]/)?.[1] ?? '';
|
|
117
117
|
defaultValue = `'${firstValue}'`;
|
|
118
118
|
}
|