@auto-engineer/server-generator-apollo-emmett 1.110.7 → 1.112.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.
Files changed (36) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-test.log +5 -5
  3. package/.turbo/turbo-type-check.log +1 -1
  4. package/CHANGELOG.md +66 -0
  5. package/README.md +0 -1
  6. package/dist/src/codegen/extract/messages.d.ts.map +1 -1
  7. package/dist/src/codegen/extract/messages.js +20 -14
  8. package/dist/src/codegen/extract/messages.js.map +1 -1
  9. package/dist/src/codegen/scaffoldFromSchema.js +2 -2
  10. package/dist/src/codegen/scaffoldFromSchema.js.map +1 -1
  11. package/dist/src/codegen/templates/command/decide.specs.specs.ts +67 -0
  12. package/dist/src/codegen/templates/command/decide.specs.ts.ejs +1 -1
  13. package/dist/src/codegen/templates/query/query.resolver.specs.ts +65 -0
  14. package/dist/src/codegen/templates/query/query.resolver.ts.ejs +15 -6
  15. package/dist/src/commands/generate-server.d.ts +0 -2
  16. package/dist/src/commands/generate-server.d.ts.map +1 -1
  17. package/dist/src/commands/generate-server.js +6 -76
  18. package/dist/src/commands/generate-server.js.map +1 -1
  19. package/dist/src/commands/initialize-server.d.ts.map +1 -1
  20. package/dist/src/commands/initialize-server.js +0 -41
  21. package/dist/src/commands/initialize-server.js.map +1 -1
  22. package/dist/tsconfig.tsbuildinfo +1 -1
  23. package/ketchup-plan.md +5 -0
  24. package/package.json +4 -4
  25. package/src/codegen/extract/messages.specs.ts +172 -0
  26. package/src/codegen/extract/messages.ts +22 -14
  27. package/src/codegen/findEventSource.specs.ts +54 -0
  28. package/src/codegen/scaffoldFromSchema.ts +2 -2
  29. package/src/codegen/templates/command/decide.specs.specs.ts +67 -0
  30. package/src/codegen/templates/command/decide.specs.ts.ejs +1 -1
  31. package/src/codegen/templates/query/query.resolver.specs.ts +65 -0
  32. package/src/codegen/templates/query/query.resolver.ts.ejs +15 -6
  33. package/src/commands/generate-server.specs.ts +1 -4
  34. package/src/commands/generate-server.ts +8 -90
  35. package/src/commands/initialize-server.specs.ts +1 -1
  36. package/src/commands/initialize-server.ts +0 -48
package/ketchup-plan.md CHANGED
@@ -6,6 +6,11 @@
6
6
 
7
7
  ## DONE (Round 3)
8
8
 
9
+ - [x] Burst 30: Fix 12 — Exclude custom GraphQL input types from enum imports in query.resolver.ts.ejs
10
+ - [x] Burst 29: Fix 11 — SKIPPED: requires modifying standalone types.ts (blocked by type-organization rule); output is identical so no behavioral impact
11
+ - [x] Burst 28: Fix 10 — Remove "valid" qualifier from decide.specs.ts.ejs test descriptions
12
+ - [x] Burst 27: Fix 9 — Map custom GraphQL input type args to GraphQLJSON in query.resolver.ts.ejs
13
+
9
14
  - [x] Burst 26: Add fart-model test for query-arg-differs-from-projection case
10
15
  - [x] Burst 25: Exclude query arg fields from projection spec expected state (6da49a58)
11
16
 
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/message-bus": "1.110.7",
36
- "@auto-engineer/narrative": "1.110.7"
35
+ "@auto-engineer/narrative": "1.112.0",
36
+ "@auto-engineer/message-bus": "1.112.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.110.7"
47
+ "@auto-engineer/cli": "1.112.0"
48
48
  },
49
- "version": "1.110.7",
49
+ "version": "1.112.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",
@@ -557,3 +557,175 @@ describe('extractMessagesFromSpecs (react slice with data target events)', () =>
557
557
  );
558
558
  });
559
559
  });
560
+
561
+ describe('extractMessagesFromSpecs (command slice with data target events)', () => {
562
+ it('should extract data.items Event not in GWT Then', () => {
563
+ const slice: Slice = {
564
+ type: 'command',
565
+ name: 'submit workout log',
566
+ server: {
567
+ description: 'Submits a workout log',
568
+ data: {
569
+ items: [
570
+ {
571
+ target: { type: 'Event', name: 'WorkoutLogged' },
572
+ destination: { type: 'stream', pattern: 'workout-${workoutId}' },
573
+ },
574
+ {
575
+ target: { type: 'Event', name: 'PointsEarned' },
576
+ destination: { type: 'stream', pattern: 'points-${userId}' },
577
+ },
578
+ {
579
+ target: { type: 'Event', name: 'WorkoutLogRejected' },
580
+ destination: { type: 'stream', pattern: 'workout-${workoutId}' },
581
+ },
582
+ ],
583
+ },
584
+ specs: [
585
+ {
586
+ type: 'gherkin',
587
+ feature: 'Submit workout log',
588
+ rules: [
589
+ {
590
+ name: 'Should submit workout',
591
+ examples: [
592
+ {
593
+ name: 'Workout logged',
594
+ steps: [
595
+ { keyword: 'When', text: 'SubmitWorkoutLog', docString: { workoutId: 'w1' } },
596
+ { keyword: 'Then', text: 'WorkoutLogged', docString: { workoutId: 'w1' } },
597
+ ],
598
+ },
599
+ {
600
+ name: 'Workout rejected',
601
+ steps: [
602
+ { keyword: 'When', text: 'SubmitWorkoutLog', docString: { workoutId: 'w2' } },
603
+ { keyword: 'Then', text: 'WorkoutLogRejected', docString: { workoutId: 'w2' } },
604
+ ],
605
+ },
606
+ ],
607
+ },
608
+ ],
609
+ },
610
+ ],
611
+ },
612
+ };
613
+
614
+ const allMessages: MessageDefinition[] = [
615
+ {
616
+ type: 'command',
617
+ name: 'SubmitWorkoutLog',
618
+ fields: [{ name: 'workoutId', type: 'string', required: true }],
619
+ },
620
+ {
621
+ type: 'event',
622
+ name: 'WorkoutLogged',
623
+ fields: [{ name: 'workoutId', type: 'string', required: true }],
624
+ },
625
+ {
626
+ type: 'event',
627
+ name: 'PointsEarned',
628
+ fields: [
629
+ { name: 'userId', type: 'string', required: true },
630
+ { name: 'points', type: 'number', required: true },
631
+ ],
632
+ },
633
+ {
634
+ type: 'event',
635
+ name: 'WorkoutLogRejected',
636
+ fields: [{ name: 'workoutId', type: 'string', required: true }],
637
+ },
638
+ ];
639
+
640
+ const result = extractMessagesFromSpecs(slice, allMessages);
641
+
642
+ expect(result.events).toEqual([
643
+ {
644
+ type: 'WorkoutLogged',
645
+ fields: [{ name: 'workoutId', tsType: 'string', required: true }],
646
+ source: 'then',
647
+ sourceFlowName: undefined,
648
+ sourceSliceName: 'submit workout log',
649
+ },
650
+ {
651
+ type: 'WorkoutLogRejected',
652
+ fields: [{ name: 'workoutId', tsType: 'string', required: true }],
653
+ source: 'then',
654
+ sourceFlowName: undefined,
655
+ sourceSliceName: 'submit workout log',
656
+ },
657
+ {
658
+ type: 'PointsEarned',
659
+ fields: [
660
+ { name: 'userId', tsType: 'string', required: true },
661
+ { name: 'points', tsType: 'number', required: true },
662
+ ],
663
+ source: 'then',
664
+ sourceSliceName: 'submit workout log',
665
+ },
666
+ ]);
667
+ });
668
+
669
+ it('should deduplicate events present in both GWT Then and data.items', () => {
670
+ const slice: Slice = {
671
+ type: 'command',
672
+ name: 'place order',
673
+ server: {
674
+ description: 'Places an order',
675
+ data: {
676
+ items: [
677
+ {
678
+ target: { type: 'Event', name: 'OrderPlaced' },
679
+ destination: { type: 'stream', pattern: 'order-${orderId}' },
680
+ },
681
+ ],
682
+ },
683
+ specs: [
684
+ {
685
+ type: 'gherkin',
686
+ feature: 'Place order',
687
+ rules: [
688
+ {
689
+ name: 'Should place',
690
+ examples: [
691
+ {
692
+ name: 'Order placed',
693
+ steps: [
694
+ { keyword: 'When', text: 'PlaceOrder', docString: { orderId: 'o1' } },
695
+ { keyword: 'Then', text: 'OrderPlaced', docString: { orderId: 'o1' } },
696
+ ],
697
+ },
698
+ ],
699
+ },
700
+ ],
701
+ },
702
+ ],
703
+ },
704
+ };
705
+
706
+ const allMessages: MessageDefinition[] = [
707
+ {
708
+ type: 'command',
709
+ name: 'PlaceOrder',
710
+ fields: [{ name: 'orderId', type: 'string', required: true }],
711
+ },
712
+ {
713
+ type: 'event',
714
+ name: 'OrderPlaced',
715
+ fields: [{ name: 'orderId', type: 'string', required: true }],
716
+ },
717
+ ];
718
+
719
+ const result = extractMessagesFromSpecs(slice, allMessages);
720
+
721
+ expect(result.events).toEqual([
722
+ {
723
+ type: 'OrderPlaced',
724
+ fields: [{ name: 'orderId', tsType: 'string', required: true }],
725
+ source: 'then',
726
+ sourceFlowName: undefined,
727
+ sourceSliceName: 'place order',
728
+ },
729
+ ]);
730
+ });
731
+ });
@@ -52,6 +52,23 @@ function deduplicateMessages<T extends Message>(messages: T[]): T[] {
52
52
  return result;
53
53
  }
54
54
 
55
+ function extractDataTargetEvents(slice: Slice, allMessages: MessageDefinition[]): Message[] {
56
+ const events: Message[] = [];
57
+ if ('server' in slice && slice.server?.data?.items) {
58
+ for (const item of slice.server.data.items) {
59
+ if (item.target.type === 'Event') {
60
+ events.push({
61
+ type: item.target.name,
62
+ fields: extractFieldsFromMessage(item.target.name, 'event', allMessages),
63
+ source: 'then' as const,
64
+ sourceSliceName: slice.name,
65
+ });
66
+ }
67
+ }
68
+ }
69
+ return events;
70
+ }
71
+
55
72
  function extractMessagesForCommand(slice: Slice, allMessages: MessageDefinition[]): ExtractedMessages {
56
73
  debugCommand('Extracting messages for command slice: %s', slice.name);
57
74
 
@@ -98,9 +115,12 @@ function extractMessagesForCommand(slice: Slice, allMessages: MessageDefinition[
98
115
  });
99
116
  debugCommand(' Total events extracted: %d', events.length);
100
117
 
118
+ const dataTargetEvents = extractDataTargetEvents(slice, allMessages);
119
+ debugCommand(' Extracted %d data target events', dataTargetEvents.length);
120
+
101
121
  const result = {
102
122
  commands,
103
- events: deduplicateMessages([...stateAsEvents, ...events]),
123
+ events: deduplicateMessages([...stateAsEvents, ...events, ...dataTargetEvents]),
104
124
  states: [],
105
125
  commandSchemasByName,
106
126
  };
@@ -215,19 +235,7 @@ function extractMessagesForReact(slice: Slice, allMessages: MessageDefinition[])
215
235
  const dataStates = extractStatesFromData(slice, allMessages);
216
236
  debugReact(' Extracted %d states from data', dataStates.length);
217
237
 
218
- const dataTargetEvents: Message[] = [];
219
- if (slice.server?.data?.items) {
220
- for (const item of slice.server.data.items) {
221
- if (item.target.type === 'Event') {
222
- dataTargetEvents.push({
223
- type: item.target.name,
224
- fields: extractFieldsFromMessage(item.target.name, 'event', allMessages),
225
- source: 'then' as const,
226
- sourceSliceName: slice.name,
227
- });
228
- }
229
- }
230
- }
238
+ const dataTargetEvents = extractDataTargetEvents(slice, allMessages);
231
239
  debugReact(' Extracted %d data target events', dataTargetEvents.length);
232
240
 
233
241
  const result = {
@@ -95,6 +95,60 @@ describe('findEventSource', () => {
95
95
  });
96
96
  });
97
97
 
98
+ it('should find event in command slice data.items when not in GWT specs', () => {
99
+ const flows: Narrative[] = [
100
+ {
101
+ name: 'workout flow',
102
+ slices: [
103
+ {
104
+ type: 'command',
105
+ name: 'submit workout log',
106
+ server: {
107
+ description: 'Submits a workout log',
108
+ data: {
109
+ items: [
110
+ {
111
+ target: { type: 'Event', name: 'WorkoutLogged' },
112
+ destination: { type: 'stream', pattern: 'workout-${workoutId}' },
113
+ },
114
+ {
115
+ target: { type: 'Event', name: 'PointsEarned' },
116
+ destination: { type: 'stream', pattern: 'points-${userId}' },
117
+ },
118
+ ],
119
+ },
120
+ specs: [
121
+ {
122
+ type: 'gherkin',
123
+ feature: 'Submit workout',
124
+ rules: [
125
+ {
126
+ name: 'Should submit',
127
+ examples: [
128
+ {
129
+ name: 'Workout logged',
130
+ steps: [
131
+ { keyword: 'When', text: 'SubmitWorkoutLog', docString: {} },
132
+ { keyword: 'Then', text: 'WorkoutLogged', docString: {} },
133
+ ],
134
+ },
135
+ ],
136
+ },
137
+ ],
138
+ },
139
+ ],
140
+ },
141
+ },
142
+ ],
143
+ },
144
+ ];
145
+
146
+ expect(findEventSource(flows, 'PointsEarned')).toEqual({
147
+ flowName: 'workout flow',
148
+ sliceName: 'submit workout log',
149
+ });
150
+ });
151
+
98
152
  it('should return null when event is not found anywhere', () => {
99
153
  const flows: Narrative[] = [
100
154
  {
@@ -742,10 +742,10 @@ export function findEventSource(flows: Narrative[], eventType: string): { flowNa
742
742
  return { flowName: flow.name, sliceName: slice.name };
743
743
  }
744
744
  }
745
- if (slice.type === 'react' && slice.server?.data?.items) {
745
+ if ('server' in slice && slice.server?.data?.items) {
746
746
  for (const item of slice.server.data.items) {
747
747
  if (item.target.type === 'Event' && item.target.name === eventType) {
748
- debugSlice(' Found event source in react data target: flow=%s, slice=%s', flow.name, slice.name);
748
+ debugSlice(' Found event source in data target: flow=%s, slice=%s', flow.name, slice.name);
749
749
  return { flowName: flow.name, sliceName: slice.name };
750
750
  }
751
751
  }
@@ -1387,4 +1387,71 @@ describe('spec.ts.ejs', () => {
1387
1387
  expect(decideFile?.contents).toContain('...command.data');
1388
1388
  expect(decideFile?.contents).not.toContain('`NotFoundError`');
1389
1389
  });
1390
+
1391
+ it('should not include "valid" qualifier in fallback test description', async () => {
1392
+ const spec: SpecsSchema = {
1393
+ variant: 'specs',
1394
+ narratives: [
1395
+ {
1396
+ name: 'order-flow',
1397
+ slices: [
1398
+ {
1399
+ type: 'command',
1400
+ name: 'place order',
1401
+ client: { specs: [] },
1402
+ server: {
1403
+ description: '',
1404
+ specs: [
1405
+ {
1406
+ type: 'gherkin',
1407
+ feature: 'Place order',
1408
+ rules: [
1409
+ {
1410
+ name: 'Order placement',
1411
+ examples: [
1412
+ {
1413
+ name: '',
1414
+ steps: [
1415
+ {
1416
+ keyword: 'When',
1417
+ text: 'PlaceOrder',
1418
+ docString: { orderId: 'o1' },
1419
+ },
1420
+ {
1421
+ keyword: 'Then',
1422
+ text: 'OrderPlaced',
1423
+ docString: { orderId: 'o1' },
1424
+ },
1425
+ ],
1426
+ },
1427
+ ],
1428
+ },
1429
+ ],
1430
+ },
1431
+ ],
1432
+ },
1433
+ },
1434
+ ],
1435
+ },
1436
+ ],
1437
+ messages: [
1438
+ {
1439
+ type: 'command',
1440
+ name: 'PlaceOrder',
1441
+ fields: [{ name: 'orderId', type: 'string', required: true }],
1442
+ },
1443
+ {
1444
+ type: 'event',
1445
+ name: 'OrderPlaced',
1446
+ fields: [{ name: 'orderId', type: 'string', required: true }],
1447
+ },
1448
+ ],
1449
+ };
1450
+
1451
+ const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
1452
+ const specFile = plans.find((p) => p.outputPath.endsWith('specs.ts'));
1453
+
1454
+ expect(specFile?.contents).toContain('should emit OrderPlaced for PlaceOrder');
1455
+ expect(specFile?.contents).not.toContain('for valid');
1456
+ });
1390
1457
  });
@@ -132,7 +132,7 @@ describe('<%= ruleDescription %>', () => {
132
132
  const testDescription = gwt.description ||
133
133
  (errorResult
134
134
  ? `should throw ${errorResult.errorType} when ${gwt.failingFields?.join(', ') || 'invalid input'}`
135
- : `should emit ${eventResults.map(e => e.eventRef).join(', ')} for valid ${commandName}`);
135
+ : `should emit ${eventResults.map(e => e.eventRef).join(', ')} for ${commandName}`);
136
136
  %>
137
137
  it('<%= testDescription %>', () => {
138
138
  given([
@@ -1186,4 +1186,69 @@ describe('query.resolver.ts.ejs', () => {
1186
1186
  "
1187
1187
  `);
1188
1188
  });
1189
+
1190
+ it('should map custom input type args to GraphQLJSON and Record<string, unknown>', async () => {
1191
+ const spec: SpecsSchema = {
1192
+ variant: 'specs',
1193
+ narratives: [
1194
+ {
1195
+ name: 'workout-flow',
1196
+ slices: [
1197
+ {
1198
+ type: 'query',
1199
+ name: 'list-workouts',
1200
+ request: `
1201
+ query ListWorkouts($filter: ListWorkoutsFilterInput, $limit: Int) {
1202
+ listWorkouts(filter: $filter, limit: $limit) {
1203
+ workoutId
1204
+ exercise
1205
+ }
1206
+ }
1207
+ `,
1208
+ client: { specs: [] },
1209
+ server: {
1210
+ description: '',
1211
+ data: {
1212
+ items: [
1213
+ {
1214
+ origin: {
1215
+ type: 'projection',
1216
+ idField: 'workoutId',
1217
+ name: 'WorkoutsProjection',
1218
+ },
1219
+ target: {
1220
+ type: 'State',
1221
+ name: 'WorkoutSummary',
1222
+ },
1223
+ },
1224
+ ],
1225
+ },
1226
+ specs: [],
1227
+ },
1228
+ },
1229
+ ],
1230
+ },
1231
+ ],
1232
+ messages: [
1233
+ {
1234
+ type: 'state',
1235
+ name: 'WorkoutSummary',
1236
+ fields: [
1237
+ { name: 'workoutId', type: 'string', required: true },
1238
+ { name: 'exercise', type: 'string', required: true },
1239
+ ],
1240
+ },
1241
+ ],
1242
+ };
1243
+
1244
+ const { plans } = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
1245
+ const resolverFile = plans.find((p) => p.outputPath.endsWith('query.resolver.ts'));
1246
+
1247
+ expect(resolverFile?.contents).toContain("import { GraphQLJSON } from 'graphql-type-json';");
1248
+ expect(resolverFile?.contents).toContain(
1249
+ "@Arg('filter', () => GraphQLJSON, { nullable: true }) filter?: Record<string, unknown>",
1250
+ );
1251
+ expect(resolverFile?.contents).toContain("@Arg('limit', () => Float, { nullable: true }) limit?: number");
1252
+ expect(resolverFile?.contents).not.toContain('ListWorkoutsFilterInput');
1253
+ });
1189
1254
  });
@@ -11,14 +11,17 @@ const usesID = parsedRequest?.args?.some(arg => graphqlType(arg.tsType) === 'ID'
11
11
 
12
12
  const messageFields = message?.fields ?? [];
13
13
  const refFields = (referencedTypes ?? []).flatMap(rt => rt.fields ?? []);
14
+ const KNOWN_GQL_SCALARS = new Set(['String', 'Int', 'Float', 'Number', 'Boolean', 'Date', 'ID']);
14
15
  const usesDate = [...messageFields, ...refFields].some(f => fieldUsesDate(f.type)) ||
15
16
  (parsedRequest?.args ?? []).some(a => fieldUsesDate(a.tsType));
17
+ const hasCustomTypeArgs = (parsedRequest?.args ?? []).some(a => !KNOWN_GQL_SCALARS.has(a.graphqlType));
16
18
  const usesJSON = [...messageFields, ...refFields].some(f => fieldUsesJSON(f.type)) ||
17
- (parsedRequest?.args ?? []).some(a => fieldUsesJSON(a.tsType));
19
+ (parsedRequest?.args ?? []).some(a => fieldUsesJSON(a.tsType)) ||
20
+ hasCustomTypeArgs;
18
21
  const usesFloat = [...messageFields, ...refFields].some(f => fieldUsesFloat(f.type)) ||
19
22
  (parsedRequest?.args ?? []).some(a => fieldUsesFloat(a.tsType));
20
23
 
21
- const enumList = collectEnumNames([...messageFields, ...(parsedRequest?.args ?? []), ...refFields]);
24
+ const enumList = collectEnumNames([...messageFields, ...refFields]);
22
25
  const filteredReferencedTypes = (referencedTypes ?? []).filter(rt => rt.name !== viewType);
23
26
 
24
27
  const embeddedTypes = [];
@@ -32,6 +35,14 @@ for (const field of messageFields) {
32
35
  }
33
36
  }
34
37
  const hasArgs = parsedRequest?.args?.length > 0;
38
+
39
+ const resolveArgTypes = (arg) => {
40
+ const isCustom = !KNOWN_GQL_SCALARS.has(arg.graphqlType);
41
+ return {
42
+ gqlType: isCustom ? 'GraphQLJSON' : graphqlType(arg.tsType),
43
+ tsType: arg.tsType === 'ID' ? 'string' : (isCustom ? 'Record<string, unknown>' : arg.tsType),
44
+ };
45
+ };
35
46
  %>
36
47
  import { Query, Resolver<% if (hasArgs) { %>, Arg<% } %>, Ctx, ObjectType, Field<% if (usesID) { %>, ID<% } %><% if (usesFloat) { %>, Float<% } %><% if (usesDate) { %>, GraphQLISODateTime<% } %> } from 'type-graphql';
37
48
  <% if (usesJSON) { %>import { GraphQLJSON } from 'graphql-type-json';
@@ -106,8 +117,7 @@ async <%= queryName %>(
106
117
  @Ctx() ctx: GraphQLContext<% if (parsedRequest?.args?.length) { %>,
107
118
  <% for (let i = 0; i < parsedRequest.args.length; i++) {
108
119
  const arg = parsedRequest.args[i];
109
- const gqlType = graphqlType(arg.tsType);
110
- const tsType = arg.tsType === 'ID' ? 'string' : arg.tsType;
120
+ const { gqlType, tsType } = resolveArgTypes(arg);
111
121
  %> @Arg('<%= arg.name %>', () => <%= gqlType %>, { nullable: true }) <%= arg.name %>?: <%= tsType %><%= i < parsedRequest.args.length - 1 ? ',' : '' %>
112
122
  <% } } %>
113
123
  ): Promise<<%= viewType %>> {
@@ -149,8 +159,7 @@ async <%= queryName %>(
149
159
  @Ctx() ctx: GraphQLContext<% if (parsedRequest?.args?.length) { %>,
150
160
  <% for (let i = 0; i < parsedRequest.args.length; i++) {
151
161
  const arg = parsedRequest.args[i];
152
- const gqlType = graphqlType(arg.tsType);
153
- const tsType = arg.tsType === 'ID' ? 'string' : arg.tsType;
162
+ const { gqlType, tsType } = resolveArgTypes(arg);
154
163
  %> @Arg('<%= arg.name %>', () => <%= gqlType %>, { nullable: true }) <%= arg.name %>?: <%= tsType %><%= i < parsedRequest.args.length - 1 ? ',' : '' %>
155
164
  <% } } %>
156
165
  ): Promise<<%= viewType %>[]> {
@@ -89,15 +89,12 @@ describe('cleanServerDir', () => {
89
89
  expect(await fs.pathExists(join(dir, 'src'))).toBe(false);
90
90
  });
91
91
 
92
- it('removes scripts/ and dist/ directories when present', async () => {
93
- await fs.ensureDir(join(dir, 'scripts'));
94
- await fs.writeFile(join(dir, 'scripts', 'generate-schema.ts'), 'export {}');
92
+ it('removes dist/ directory when present', async () => {
95
93
  await fs.ensureDir(join(dir, 'dist'));
96
94
  await fs.writeFile(join(dir, 'dist', 'server.js'), 'export {}');
97
95
 
98
96
  await cleanServerDir(dir);
99
97
 
100
- expect(await fs.pathExists(join(dir, 'scripts'))).toBe(false);
101
98
  expect(await fs.pathExists(join(dir, 'dist'))).toBe(false);
102
99
  });
103
100
  });