@auto-engineer/server-generator-apollo-emmett 1.63.0 → 1.64.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/ketchup-plan.md CHANGED
@@ -1,9 +1,7 @@
1
- # Ketchup Plan: `{exercise:string: number}` malformed map type crashes Prettier
1
+ # Ketchup Plan: Fix stale enums in shared/types.ts
2
2
 
3
3
  ## TODO
4
4
 
5
5
  ## DONE
6
6
 
7
- - [x] Burst 1: `sanitizeFieldType` helper + tests (a90a25a2)
8
- - [x] Burst 2: Refactor `SkippedFieldInfo` → `FieldIssue` (da36c911)
9
- - [x] Burst 3: Wire `sanitizeFieldType` + emit event for sanitized types (ba39eefe)
7
+ - [x] Burst 1: Replace appendEnumsToSharedTypes with from-scratch buildSharedTypesContent + writeSharedTypes + tests [depends: none] (4bc755b4)
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.63.0",
36
- "@auto-engineer/message-bus": "1.63.0"
35
+ "@auto-engineer/narrative": "1.64.0",
36
+ "@auto-engineer/message-bus": "1.64.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.63.0"
47
+ "@auto-engineer/cli": "1.64.0"
48
48
  },
49
- "version": "1.63.0",
49
+ "version": "1.64.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",
@@ -0,0 +1,89 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { buildSharedTypesContent, type EnumDefinition } from './scaffoldFromSchema';
3
+
4
+ const SHARED_TYPES_BASE = `import 'reflect-metadata';
5
+ import type { CommandSender, EventStore, InMemoryDatabase } from '@event-driven-io/emmett';
6
+
7
+ export interface ReactorContext {
8
+ eventStore: EventStore;
9
+ commandSender: CommandSender;
10
+ database: InMemoryDatabase;
11
+ [key: string]: unknown;
12
+ }
13
+
14
+ export interface GraphQLContext {
15
+ eventStore: EventStore;
16
+ messageBus: CommandSender;
17
+ database: InMemoryDatabase;
18
+ }`;
19
+
20
+ describe('buildSharedTypesContent', () => {
21
+ it('returns base template with no enum imports when enums is empty', () => {
22
+ const result = buildSharedTypesContent([]);
23
+
24
+ expect(result).toEqual(`${SHARED_TYPES_BASE}\n`);
25
+ });
26
+
27
+ it('includes registerEnumType import and enum code for a single enum', () => {
28
+ const enums: EnumDefinition[] = [
29
+ { name: 'Status', values: ['active', 'inactive'], unionString: "'active' | 'inactive'" },
30
+ ];
31
+
32
+ const result = buildSharedTypesContent(enums);
33
+
34
+ expect(result).toEqual(`import { registerEnumType } from 'type-graphql';
35
+
36
+ ${SHARED_TYPES_BASE}
37
+
38
+ export enum Status {
39
+ ACTIVE = 'active',
40
+ INACTIVE = 'inactive',
41
+ }
42
+
43
+ registerEnumType(Status, {
44
+ name: 'Status',
45
+ });
46
+ `);
47
+ });
48
+
49
+ it('includes all enums when given multiple enum definitions', () => {
50
+ const enums: EnumDefinition[] = [
51
+ { name: 'Status', values: ['active', 'inactive'], unionString: "'active' | 'inactive'" },
52
+ { name: 'Priority', values: ['low', 'high'], unionString: "'high' | 'low'" },
53
+ ];
54
+
55
+ const result = buildSharedTypesContent(enums);
56
+
57
+ expect(result).toEqual(`import { registerEnumType } from 'type-graphql';
58
+
59
+ ${SHARED_TYPES_BASE}
60
+
61
+ export enum Status {
62
+ ACTIVE = 'active',
63
+ INACTIVE = 'inactive',
64
+ }
65
+
66
+ registerEnumType(Status, {
67
+ name: 'Status',
68
+ });
69
+
70
+ export enum Priority {
71
+ LOW = 'low',
72
+ HIGH = 'high',
73
+ }
74
+
75
+ registerEnumType(Priority, {
76
+ name: 'Priority',
77
+ });
78
+ `);
79
+ });
80
+
81
+ it('produces identical output for the same input across multiple calls', () => {
82
+ const enums: EnumDefinition[] = [{ name: 'Color', values: ['red', 'blue'], unionString: "'blue' | 'red'" }];
83
+
84
+ const first = buildSharedTypesContent(enums);
85
+ const second = buildSharedTypesContent(enums);
86
+
87
+ expect(first).toEqual(second);
88
+ });
89
+ });
@@ -59,7 +59,7 @@ const defaultFilesByType: Record<string, string[]> = {
59
59
  react: ['events.ts.ejs', 'react.ts.ejs', 'react.specs.ts.ejs', 'register.ts.ejs'],
60
60
  };
61
61
 
62
- interface EnumDefinition {
62
+ export interface EnumDefinition {
63
63
  name: string;
64
64
  values: string[];
65
65
  unionString: string;
@@ -188,64 +188,38 @@ registerEnumType(${enumDef.name}, {
188
188
  });`;
189
189
  }
190
190
 
191
- async function appendEnumsToSharedTypes(baseDir: string, enums: EnumDefinition[]): Promise<void> {
192
- if (enums.length === 0) return;
191
+ const SHARED_TYPES_BASE = `import 'reflect-metadata';
192
+ import type { CommandSender, EventStore, InMemoryDatabase } from '@event-driven-io/emmett';
193
193
 
194
- const sharedTypesPath = path.join(baseDir, 'shared', 'types.ts');
195
-
196
- let existingContent = '';
197
- try {
198
- existingContent = await fs.readFile(sharedTypesPath, 'utf8');
199
- } catch {
200
- debug('Types file does not exist yet at %s, will create it', sharedTypesPath);
201
- }
194
+ export interface ReactorContext {
195
+ eventStore: EventStore;
196
+ commandSender: CommandSender;
197
+ database: InMemoryDatabase;
198
+ [key: string]: unknown;
199
+ }
202
200
 
203
- const enumsToAdd = enums.filter((e) => {
204
- const enumPattern = new RegExp(`export\\s+enum\\s+${e.name}\\s*\\{`, 'm');
205
- return !enumPattern.test(existingContent);
206
- });
201
+ export interface GraphQLContext {
202
+ eventStore: EventStore;
203
+ messageBus: CommandSender;
204
+ database: InMemoryDatabase;
205
+ }`;
207
206
 
208
- if (enumsToAdd.length === 0) {
209
- debug('All enums already exist in %s, skipping', sharedTypesPath);
210
- return;
207
+ export function buildSharedTypesContent(enums: EnumDefinition[]): string {
208
+ if (enums.length === 0) {
209
+ return `${SHARED_TYPES_BASE}\n`;
211
210
  }
212
211
 
213
- debug('Adding %d new enums to %s', enumsToAdd.length, sharedTypesPath);
214
-
215
- const hasRegisterEnumImport = existingContent.includes('registerEnumType');
216
- const enumCode = enumsToAdd.map((e) => generateEnumTypeScript(e)).join('\n\n');
217
-
218
- let newContent: string;
219
- if (hasRegisterEnumImport) {
220
- newContent = `${existingContent.trimEnd()}\n\n${enumCode}\n`;
221
- } else {
222
- const importMatch = existingContent.match(/^([\s\S]*?)(import.*from\s+['"]type-graphql['"];?\s*\n)/m);
223
- if (importMatch !== null) {
224
- const beforeImport = importMatch[1];
225
- const typeGraphqlImport = importMatch[2];
226
- const afterImport = existingContent.slice(beforeImport.length + typeGraphqlImport.length);
227
-
228
- const updatedImport = typeGraphqlImport.replace(
229
- /^import\s*\{([^}]*)\}\s*from\s*['"]type-graphql['"];?\s*$/m,
230
- (_match: string, imports: string): string => {
231
- const importsList = imports
232
- .split(',')
233
- .map((s) => s.trim())
234
- .filter((item) => item.length > 0);
235
- if (!importsList.includes('registerEnumType')) {
236
- importsList.push('registerEnumType');
237
- }
238
- return `import { ${importsList.join(', ')} } from 'type-graphql';`;
239
- },
240
- );
212
+ const enumCode = enums.map((e) => generateEnumTypeScript(e)).join('\n\n');
213
+ return `import { registerEnumType } from 'type-graphql';\n\n${SHARED_TYPES_BASE}\n\n${enumCode}\n`;
214
+ }
241
215
 
242
- newContent = `${beforeImport}${updatedImport}${afterImport.trimEnd()}\n\n${enumCode}\n`;
243
- } else {
244
- newContent = `import { registerEnumType } from 'type-graphql';\n\n${existingContent.trimEnd()}\n\n${enumCode}\n`;
245
- }
246
- }
216
+ async function writeSharedTypes(baseDir: string, enums: EnumDefinition[]): Promise<void> {
217
+ const sharedTypesPath = path.join(baseDir, 'shared', 'types.ts');
218
+ debug('Writing shared types to %s with %d enums', sharedTypesPath, enums.length);
219
+
220
+ const content = buildSharedTypesContent(enums);
247
221
 
248
- const formatted = await prettier.format(newContent, {
222
+ const formatted = await prettier.format(content, {
249
223
  parser: 'typescript',
250
224
  filepath: sharedTypesPath,
251
225
  singleQuote: true,
@@ -256,7 +230,6 @@ async function appendEnumsToSharedTypes(baseDir: string, enums: EnumDefinition[]
256
230
 
257
231
  await fs.mkdir(path.dirname(sharedTypesPath), { recursive: true });
258
232
  await fs.writeFile(sharedTypesPath, formatted, 'utf8');
259
- debug('Appended %d enums to %s', enums.length, sharedTypesPath);
260
233
  }
261
234
 
262
235
  export interface FilePlan {
@@ -878,7 +851,7 @@ export async function generateScaffoldFilePlans(
878
851
  debug(' Extracted %d enums from messages', enums.length);
879
852
 
880
853
  const domainBaseDir = baseDir.replace(/\/flows$/, '');
881
- await appendEnumsToSharedTypes(domainBaseDir, enums);
854
+ await writeSharedTypes(domainBaseDir, enums);
882
855
 
883
856
  const allPlans: FilePlan[] = [];
884
857
  const allDuplicateCommands: DuplicateCommandInfo[] = [];
@@ -1,5 +1,3 @@
1
- import { registerEnumType } from 'type-graphql';
2
-
3
1
  import 'reflect-metadata';
4
2
  import type { CommandSender, EventStore, InMemoryDatabase } from '@event-driven-io/emmett';
5
3
 
@@ -15,13 +13,3 @@ export interface GraphQLContext {
15
13
  messageBus: CommandSender;
16
14
  database: InMemoryDatabase;
17
15
  }
18
-
19
- export enum QuestionnaireProgressStatus {
20
- IN_PROGRESS = 'in_progress',
21
- READY_TO_SUBMIT = 'ready_to_submit',
22
- SUBMITTED = 'submitted',
23
- }
24
-
25
- registerEnumType(QuestionnaireProgressStatus, {
26
- name: 'QuestionnaireProgressStatus',
27
- });