@auto-engineer/server-generator-apollo-emmett 0.8.5 → 0.8.6

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 (148) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/CHANGELOG.md +8 -0
  3. package/dist/src/codegen/scaffoldFromSchema.d.ts +3 -3
  4. package/dist/src/codegen/scaffoldFromSchema.d.ts.map +1 -1
  5. package/dist/src/codegen/scaffoldFromSchema.js +54 -18
  6. package/dist/src/codegen/scaffoldFromSchema.js.map +1 -1
  7. package/dist/{codegen → src/codegen}/templates/command/commands.specs.ts +1 -2
  8. package/dist/{codegen → src/codegen}/templates/command/decide.specs.specs.ts +3 -3
  9. package/dist/{codegen → src/codegen}/templates/command/decide.specs.ts +1 -5
  10. package/dist/{codegen → src/codegen}/templates/command/events.specs.ts +1 -2
  11. package/dist/{codegen → src/codegen}/templates/command/evolve.specs.ts +1 -2
  12. package/dist/{codegen → src/codegen}/templates/command/handle.specs.ts +1 -3
  13. package/dist/src/codegen/templates/command/mutation.resolver.specs.ts +363 -0
  14. package/dist/src/codegen/templates/command/mutation.resolver.ts.ejs +99 -0
  15. package/dist/{codegen → src/codegen}/templates/command/register.specs.ts +1 -2
  16. package/dist/{codegen → src/codegen}/templates/command/state.specs.ts +1 -2
  17. package/dist/{codegen → src/codegen}/templates/query/projection.specs.specs..ts +3 -3
  18. package/dist/{codegen → src/codegen}/templates/query/projection.specs.ts +5 -9
  19. package/dist/{codegen → src/codegen}/templates/query/query.resolver.specs.ts +194 -23
  20. package/dist/src/codegen/templates/query/query.resolver.ts.ejs +137 -0
  21. package/dist/{codegen → src/codegen}/templates/query/state.specs.ts +1 -2
  22. package/dist/{codegen → src/codegen}/templates/react/react.specs.specs.ts +2 -2
  23. package/dist/{codegen → src/codegen}/templates/react/react.specs.ts +3 -3
  24. package/dist/{codegen → src/codegen}/templates/react/register.specs.ts +3 -3
  25. package/dist/src/codegen/test-data/specVariant1.d.ts +1 -1
  26. package/dist/src/codegen/test-data/specVariant1.d.ts.map +1 -1
  27. package/dist/src/codegen/test-data/specVariant1.js +4 -2
  28. package/dist/src/codegen/test-data/specVariant1.js.map +1 -1
  29. package/dist/src/commands/generate-server.d.ts.map +1 -1
  30. package/dist/src/commands/generate-server.js +34 -24
  31. package/dist/src/commands/generate-server.js.map +1 -1
  32. package/dist/tsconfig.tsbuildinfo +1 -1
  33. package/package.json +8 -7
  34. package/src/codegen/scaffoldFromSchema.ts +52 -20
  35. package/src/codegen/templates/command/commands.specs.ts +1 -2
  36. package/src/codegen/templates/command/decide.specs.specs.ts +3 -3
  37. package/src/codegen/templates/command/decide.specs.ts +1 -5
  38. package/src/codegen/templates/command/events.specs.ts +1 -2
  39. package/src/codegen/templates/command/evolve.specs.ts +1 -2
  40. package/src/codegen/templates/command/handle.specs.ts +1 -3
  41. package/src/codegen/templates/command/mutation.resolver.specs.ts +247 -8
  42. package/src/codegen/templates/command/mutation.resolver.ts.ejs +86 -12
  43. package/src/codegen/templates/command/register.specs.ts +1 -2
  44. package/src/codegen/templates/command/state.specs.ts +1 -2
  45. package/src/codegen/templates/query/projection.specs.specs..ts +3 -3
  46. package/src/codegen/templates/query/projection.specs.ts +5 -9
  47. package/src/codegen/templates/query/query.resolver.specs.ts +194 -23
  48. package/src/codegen/templates/query/query.resolver.ts.ejs +84 -45
  49. package/src/codegen/templates/query/state.specs.ts +1 -2
  50. package/src/codegen/templates/react/react.specs.specs.ts +2 -2
  51. package/src/codegen/templates/react/react.specs.ts +3 -3
  52. package/src/codegen/templates/react/register.specs.ts +3 -3
  53. package/src/codegen/test-data/specVariant1.ts +5 -3
  54. package/src/commands/generate-server.ts +38 -30
  55. package/tsconfig.test.json +9 -0
  56. package/.turbo/turbo-test.log +0 -14
  57. package/.turbo/turbo-type-check.log +0 -4
  58. package/dist/codegen/templates/command/mutation.resolver.specs.ts +0 -124
  59. package/dist/codegen/templates/command/mutation.resolver.ts.ejs +0 -25
  60. package/dist/codegen/templates/query/query.resolver.ts.ejs +0 -98
  61. package/dist/src/codegen/templates/command/commands.specs.d.ts +0 -2
  62. package/dist/src/codegen/templates/command/commands.specs.d.ts.map +0 -1
  63. package/dist/src/codegen/templates/command/commands.specs.js +0 -96
  64. package/dist/src/codegen/templates/command/commands.specs.js.map +0 -1
  65. package/dist/src/codegen/templates/command/decide.specs.d.ts +0 -2
  66. package/dist/src/codegen/templates/command/decide.specs.d.ts.map +0 -1
  67. package/dist/src/codegen/templates/command/decide.specs.js +0 -567
  68. package/dist/src/codegen/templates/command/decide.specs.js.map +0 -1
  69. package/dist/src/codegen/templates/command/decide.specs.specs.d.ts +0 -2
  70. package/dist/src/codegen/templates/command/decide.specs.specs.d.ts.map +0 -1
  71. package/dist/src/codegen/templates/command/decide.specs.specs.js +0 -278
  72. package/dist/src/codegen/templates/command/decide.specs.specs.js.map +0 -1
  73. package/dist/src/codegen/templates/command/events.specs.d.ts +0 -2
  74. package/dist/src/codegen/templates/command/events.specs.d.ts.map +0 -1
  75. package/dist/src/codegen/templates/command/events.specs.js +0 -112
  76. package/dist/src/codegen/templates/command/events.specs.js.map +0 -1
  77. package/dist/src/codegen/templates/command/evolve.specs.d.ts +0 -2
  78. package/dist/src/codegen/templates/command/evolve.specs.d.ts.map +0 -1
  79. package/dist/src/codegen/templates/command/evolve.specs.js +0 -108
  80. package/dist/src/codegen/templates/command/evolve.specs.js.map +0 -1
  81. package/dist/src/codegen/templates/command/handle.specs.d.ts +0 -2
  82. package/dist/src/codegen/templates/command/handle.specs.d.ts.map +0 -1
  83. package/dist/src/codegen/templates/command/handle.specs.js +0 -326
  84. package/dist/src/codegen/templates/command/handle.specs.js.map +0 -1
  85. package/dist/src/codegen/templates/command/mutation.resolver.specs.d.ts +0 -2
  86. package/dist/src/codegen/templates/command/mutation.resolver.specs.d.ts.map +0 -1
  87. package/dist/src/codegen/templates/command/mutation.resolver.specs.js +0 -121
  88. package/dist/src/codegen/templates/command/mutation.resolver.specs.js.map +0 -1
  89. package/dist/src/codegen/templates/command/register.specs.d.ts +0 -2
  90. package/dist/src/codegen/templates/command/register.specs.d.ts.map +0 -1
  91. package/dist/src/codegen/templates/command/register.specs.js +0 -113
  92. package/dist/src/codegen/templates/command/register.specs.js.map +0 -1
  93. package/dist/src/codegen/templates/command/state.specs.d.ts +0 -2
  94. package/dist/src/codegen/templates/command/state.specs.d.ts.map +0 -1
  95. package/dist/src/codegen/templates/command/state.specs.js +0 -133
  96. package/dist/src/codegen/templates/command/state.specs.js.map +0 -1
  97. package/dist/src/codegen/templates/query/projection.specs.d.ts +0 -2
  98. package/dist/src/codegen/templates/query/projection.specs.d.ts.map +0 -1
  99. package/dist/src/codegen/templates/query/projection.specs.js +0 -363
  100. package/dist/src/codegen/templates/query/projection.specs.js.map +0 -1
  101. package/dist/src/codegen/templates/query/projection.specs.specs..d.ts +0 -2
  102. package/dist/src/codegen/templates/query/projection.specs.specs..d.ts.map +0 -1
  103. package/dist/src/codegen/templates/query/projection.specs.specs..js +0 -293
  104. package/dist/src/codegen/templates/query/projection.specs.specs..js.map +0 -1
  105. package/dist/src/codegen/templates/query/query.resolver.specs.d.ts +0 -2
  106. package/dist/src/codegen/templates/query/query.resolver.specs.d.ts.map +0 -1
  107. package/dist/src/codegen/templates/query/query.resolver.specs.js +0 -249
  108. package/dist/src/codegen/templates/query/query.resolver.specs.js.map +0 -1
  109. package/dist/src/codegen/templates/query/state.specs.d.ts +0 -2
  110. package/dist/src/codegen/templates/query/state.specs.d.ts.map +0 -1
  111. package/dist/src/codegen/templates/query/state.specs.js +0 -67
  112. package/dist/src/codegen/templates/query/state.specs.js.map +0 -1
  113. package/dist/src/codegen/templates/react/react.specs.d.ts +0 -2
  114. package/dist/src/codegen/templates/react/react.specs.d.ts.map +0 -1
  115. package/dist/src/codegen/templates/react/react.specs.js +0 -265
  116. package/dist/src/codegen/templates/react/react.specs.js.map +0 -1
  117. package/dist/src/codegen/templates/react/react.specs.specs.d.ts +0 -2
  118. package/dist/src/codegen/templates/react/react.specs.specs.d.ts.map +0 -1
  119. package/dist/src/codegen/templates/react/react.specs.specs.js +0 -229
  120. package/dist/src/codegen/templates/react/react.specs.specs.js.map +0 -1
  121. package/dist/src/codegen/templates/react/register.specs.d.ts +0 -2
  122. package/dist/src/codegen/templates/react/register.specs.d.ts.map +0 -1
  123. package/dist/src/codegen/templates/react/register.specs.js +0 -246
  124. package/dist/src/codegen/templates/react/register.specs.js.map +0 -1
  125. /package/dist/{codegen → src/codegen}/templates/command/commands.ts.ejs +0 -0
  126. /package/dist/{codegen → src/codegen}/templates/command/decide.specs.ts.ejs +0 -0
  127. /package/dist/{codegen → src/codegen}/templates/command/decide.ts.ejs +0 -0
  128. /package/dist/{codegen → src/codegen}/templates/command/events.ts.ejs +0 -0
  129. /package/dist/{codegen → src/codegen}/templates/command/evolve.ts.ejs +0 -0
  130. /package/dist/{codegen → src/codegen}/templates/command/handle.ts.ejs +0 -0
  131. /package/dist/{codegen → src/codegen}/templates/command/register.ts.ejs +0 -0
  132. /package/dist/{codegen → src/codegen}/templates/command/state.ts.ejs +0 -0
  133. /package/dist/{codegen → src/codegen}/templates/query/projection.specs.ts.ejs +0 -0
  134. /package/dist/{codegen → src/codegen}/templates/query/projection.ts.ejs +0 -0
  135. /package/dist/{codegen → src/codegen}/templates/query/state.ts.ejs +0 -0
  136. /package/dist/{codegen → src/codegen}/templates/react/react.specs.ts.ejs +0 -0
  137. /package/dist/{codegen → src/codegen}/templates/react/react.ts.ejs +0 -0
  138. /package/dist/{codegen → src/codegen}/templates/react/register.ts.ejs +0 -0
  139. /package/dist/{domain → src/domain}/shared/ReadModel.ts +0 -0
  140. /package/dist/{domain → src/domain}/shared/index.ts +0 -0
  141. /package/dist/{domain → src/domain}/shared/reactorSpecification.ts +0 -0
  142. /package/dist/{domain → src/domain}/shared/sendCommand.ts +0 -0
  143. /package/dist/{domain → src/domain}/shared/types.ts +0 -0
  144. /package/dist/{server.ts → src/server.ts} +0 -0
  145. /package/dist/{utils → src/utils}/index.ts +0 -0
  146. /package/dist/{utils → src/utils}/loadProjections.ts +0 -0
  147. /package/dist/{utils → src/utils}/loadRegisterFiles.ts +0 -0
  148. /package/dist/{utils → src/utils}/loadResolvers.ts +0 -0
@@ -8,90 +8,129 @@ const message = messages?.find(m => m.name === viewType);
8
8
  const resolverClassName = `${pascalCase(slice.name)}QueryResolver`;
9
9
  const usesID = parsedRequest?.args?.some(arg => graphqlType(arg.tsType) === 'ID');
10
10
 
11
+ function isInlineObject(ts) {
12
+ return /^\{[\s\S]*\}$/.test((ts ?? '').trim());
13
+ }
14
+ function isInlineObjectArray(ts) {
15
+ const t = (ts ?? '').trim();
16
+ return /^Array<\{[\s\S]*\}>$/.test(t) || /^\{[\s\S]*\}\[\]$/.test(t);
17
+ }
18
+ function baseTs(ts) {
19
+ return (ts ?? 'string').replace(/\s*\|\s*null\b/g, '').trim();
20
+ }
21
+ function fieldUsesDate(ts) {
22
+ const b = baseTs(ts);
23
+ if (b === 'Date') return true;
24
+ if (isInlineObject(b) || isInlineObjectArray(b)) return /:\s*Date\b/.test(b);
25
+ return false;
26
+ }
27
+ function fieldUsesJSON(ts) {
28
+ const b = baseTs(ts);
29
+ if (b === 'unknown' || b === 'any' || b === 'object') return true;
30
+ if (isInlineObject(b) || isInlineObjectArray(b)) return /:\s*(unknown|any|object)\b/.test(b);
31
+ return false;
32
+ }
33
+
34
+ const messageFields = message?.fields ?? [];
35
+ const usesDate = messageFields.some(f => fieldUsesDate(f.type)) ||
36
+ (parsedRequest?.args ?? []).some(a => baseTs(a.tsType) === 'Date');
37
+ const usesJSON = messageFields.some(f => fieldUsesJSON(f.type));
38
+
39
+ // Collect embedded types up-front so we can emit them before the parent
11
40
  const embeddedTypes = [];
12
- function getEmbeddedObjectTypeName(parentName, fieldName) {
13
- return `${parentName}${pascalCase(fieldName)}`;
41
+ for (const field of messageFields) {
42
+ const tsType = field.type ?? 'string';
43
+ if (isInlineObjectArray(tsType) || isInlineObject(tsType)) {
44
+ embeddedTypes.push({
45
+ typeName: `${viewType}${pascalCase(field.name)}`,
46
+ tsType,
47
+ });
48
+ }
14
49
  }
15
50
  %>
16
- import { Query, Resolver, Arg, Ctx, ObjectType, Field<% if (usesID) { %>, ID<% } %> } from 'type-graphql';
17
- import { type GraphQLContext, ReadModel } from '../../../shared';
51
+ import { Query, Resolver, Arg, Ctx, ObjectType, Field<% if (usesID) { %>, ID<% } %><% if (usesDate) { %>, GraphQLISODateTime<% } %> } from 'type-graphql';
52
+ <% if (usesJSON) { %>import { GraphQLJSON } from 'graphql-type-json';
53
+ <% } %>import { type GraphQLContext, ReadModel } from '../../../shared';
54
+
55
+ <% // Emit embedded types FIRST — this matches your snapshot order
56
+ for (const { typeName, tsType } of embeddedTypes) {
57
+ // Extract inner "{ ... }" whether Array<{...}> or "{...}[]"
58
+ const inner = tsType.trim().startsWith('Array<')
59
+ ? tsType.trim().replace(/^Array<\{/, '{').replace(/}>$/, '}')
60
+ : tsType.trim().replace(/\[\]$/, '');
61
+ const match = inner.match(/^\{([\s\S]*)\}$/);
62
+ const body = match ? match[1] : '';
63
+ const rawFields = body.split(/[,;]\s*/).filter(Boolean);
64
+ const parsedFields = rawFields.map(f => {
65
+ const parts = f.split(':');
66
+ const name = parts[0]?.trim();
67
+ const type = parts.slice(1).join(':').trim();
68
+ if (!name || !type) return null;
69
+ return { name, tsType: type, gqlType: graphqlType(type), nullable: isNullable(type) };
70
+ }).filter(Boolean);
71
+ %>
72
+ @ObjectType()
73
+ export class <%= typeName %> {
74
+ <% for (const f of parsedFields) { %>
75
+ @Field(() => <%= f.gqlType %><%= f.nullable ? ', { nullable: true }' : '' %>)
76
+ <%= f.name %><%= f.nullable ? '?' : '!' %>: <%= toTsFieldType(f.tsType) %>;
77
+ <% } %>
78
+ }
79
+ <% } %>
18
80
 
19
81
  @ObjectType()
20
82
  export class <%= viewType %> {
21
- <% if (message?.fields?.length) {
22
- for (const field of message.fields) {
23
- const gqlType = graphqlType(field.type ?? 'string');
83
+ <% if (messageFields.length) {
84
+ for (const field of messageFields) {
24
85
  const tsType = field.type ?? 'string';
25
- const isInlineArray = tsType.startsWith('Array<{') || tsType.startsWith('{') || tsType.includes('[]');
26
- const fieldTypeName = getEmbeddedObjectTypeName(viewType, field.name);
27
-
28
- if (isInlineArray) {
29
- embeddedTypes.push({ parentName: viewType, fieldName: field.name, typeName: fieldTypeName, tsType });
86
+ const gqlType = graphqlType(tsType);
87
+ const typeName = `${viewType}${pascalCase(field.name)}`;
30
88
  %>
31
- @Field(() => [<%= fieldTypeName %>])
32
- <%= field.name %>!: <%= fieldTypeName %>[];
33
- <% } else { %>
34
- @Field(() => <%= gqlType %>)
35
- <%= field.name %>!: <%= tsType %>;
36
- <% }
37
- } %>
89
+ <% if (isInlineObjectArray(tsType)) { %>
90
+ @Field(() => [<%= typeName %>])
91
+ <%= field.name %>!: <%= typeName %>[];
92
+ <% } else if (isInlineObject(tsType)) { %>
93
+ @Field(() => <%= typeName %>)
94
+ <%= field.name %>!: <%= typeName %>;
95
+ <% } else { %>
96
+ @Field(() => <%= gqlType %><%= isNullable(tsType) ? ', { nullable: true }' : '' %>)
97
+ <%= field.name %><%= isNullable(tsType) ? '?' : '!' %>: <%= toTsFieldType(tsType) %>;
98
+ <% } } %>
38
99
  [key: string]: unknown;
39
100
  <% } else { %>
40
101
  [key: string]: unknown;
41
102
  <% } %>
42
103
  }
43
104
 
44
- <% for (const { typeName, tsType } of embeddedTypes) {
45
- const match = tsType.match(/Array<\{([^}]*)\}>/);
46
- const rawFields = match?.[1]?.split(/[,;]/) ?? [];
47
- const parsedFields = rawFields
48
- .map(f => {
49
- const [name, type] = f.trim().split(':').map(x => x.trim());
50
- if (!name || !type) return null;
51
- return { name, tsType: type, gqlType: graphqlType(type) };
52
- })
53
- .filter(Boolean);
54
- %>
55
-
56
- @ObjectType()
57
- export class <%= typeName %> {
58
- <% for (const f of parsedFields) { %>
59
- @Field(() => <%= f.gqlType %>)
60
- <%= f.name %>!: <%= f.tsType %>;
61
- <% } %>
62
- }
63
- <% } %>
64
-
65
105
  @Resolver()
66
106
  export class <%= resolverClassName %> {
67
107
  @Query(() => [<%= viewType %>])
68
108
  async <%= queryName %>(
69
109
  @Ctx() ctx: GraphQLContext<% if (parsedRequest?.args?.length) { %>,
70
- <% for (let i = 0; i < parsedRequest.args.length; i++) {
110
+ <% for (let i = 0; i < parsedRequest.args.length; i++) {
71
111
  const arg = parsedRequest.args[i];
72
112
  const gqlType = graphqlType(arg.tsType);
73
113
  const tsType = arg.tsType === 'ID' ? 'string' : arg.tsType;
74
114
  %> @Arg('<%= arg.name %>', () => <%= gqlType %>, { nullable: true }) <%= arg.name %>?: <%= tsType %><%= i < parsedRequest.args.length - 1 ? ',' : '' %>
75
- <% } } %>
115
+ <% } } %>
76
116
  ): Promise<<%= viewType %>[]> {
77
117
  const model = new ReadModel<<%= viewType %>>(ctx.eventStore, '<%= projectionType %>');
78
118
 
79
119
  // ## IMPLEMENTATION INSTRUCTIONS ##
80
120
  // You can query the projection using the ReadModel API:
81
- //
82
121
  // - model.getAll() — fetch all documents
83
122
  // - model.getById(id) — fetch a single document by ID (default key: 'id')
84
123
  // - model.find(filterFn) — filter documents using a predicate
85
124
  // - model.first(filterFn) — fetch the first document matching a predicate
86
125
  //
87
- // Example below uses `.find()` to filter
126
+ // Example below uses \`.find()\` to filter
88
127
  // change the logic for the query as needed to meet the requirements for the current slice.
89
128
 
90
129
  return model.find((item) => {
91
130
  <% if (parsedRequest?.args?.length) {
92
131
  for (const arg of parsedRequest.args) { %>
93
132
  if (<%= arg.name %> !== undefined && item.<%= arg.name %> !== <%= arg.name %>) return false;
94
- <% } } %>
133
+ <% } } %>
95
134
  return true;
96
135
  });
97
136
  }
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { generateScaffoldFilePlans } from '../../scaffoldFromSchema';
3
- import { SpecsSchemaType as SpecsSchema } from '@auto-engineer/flow';
3
+ import { Model as SpecsSchema } from '@auto-engineer/flow';
4
4
 
5
5
  describe('state.ts.ejs', () => {
6
6
  it('should generate a valid state definition file for a query slice with a state message', async () => {
@@ -15,7 +15,6 @@ describe('state.ts.ejs', () => {
15
15
  name: 'Get available items',
16
16
  client: {
17
17
  description: 'Client view of available items',
18
- specs: [],
19
18
  },
20
19
  server: {
21
20
  description: 'Projects available items',
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { SpecsSchemaType as SpecsSchema } from '@auto-engineer/flow';
2
+ import { Model as SpecsSchema } from '@auto-engineer/flow';
3
3
  import { generateScaffoldFilePlans } from '../../scaffoldFromSchema';
4
4
 
5
5
  describe('react.specs.ts.ejs (react slice)', () => {
@@ -13,7 +13,7 @@ describe('react.specs.ts.ejs (react slice)', () => {
13
13
  {
14
14
  type: 'command',
15
15
  name: 'guest submits booking request',
16
- client: { description: '', specs: [] },
16
+ client: { description: '' },
17
17
  server: {
18
18
  description: '',
19
19
  specs: {
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { SpecsSchemaType as SpecsSchema } from '@auto-engineer/flow';
2
+ import { Model as SpecsSchema } from '@auto-engineer/flow';
3
3
  import { generateScaffoldFilePlans } from '../../scaffoldFromSchema';
4
4
 
5
5
  describe('handle.ts.ejs (react slice)', () => {
@@ -13,7 +13,7 @@ describe('handle.ts.ejs (react slice)', () => {
13
13
  {
14
14
  type: 'command',
15
15
  name: 'guest submits booking request',
16
- client: { description: '', specs: [] },
16
+ client: { description: '' },
17
17
  server: {
18
18
  description: '',
19
19
  specs: {
@@ -116,7 +116,7 @@ describe('handle.ts.ejs (react slice)', () => {
116
116
  {
117
117
  type: 'command',
118
118
  name: 'notify host',
119
- client: { description: '', specs: [] },
119
+ client: { description: '' },
120
120
  server: {
121
121
  description: '',
122
122
  specs: {
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { SpecsSchemaType as SpecsSchema } from '@auto-engineer/flow';
2
+ import { Model as SpecsSchema } from '@auto-engineer/flow';
3
3
  import { generateScaffoldFilePlans } from '../../scaffoldFromSchema';
4
4
 
5
5
  describe('register.ts.ejs (react slice)', () => {
@@ -13,7 +13,7 @@ describe('register.ts.ejs (react slice)', () => {
13
13
  {
14
14
  type: 'command',
15
15
  name: 'guest submits booking request',
16
- client: { description: '', specs: [] },
16
+ client: { description: '' },
17
17
  server: {
18
18
  description: '',
19
19
  specs: {
@@ -116,7 +116,7 @@ describe('register.ts.ejs (react slice)', () => {
116
116
  {
117
117
  type: 'command',
118
118
  name: 'notify host',
119
- client: { description: '', specs: [] },
119
+ client: { description: '' },
120
120
  server: {
121
121
  description: '',
122
122
  specs: {
@@ -1,4 +1,4 @@
1
- import { SpecsSchemaType as SpecsSchema } from '@auto-engineer/flow';
1
+ import { Model as SpecsSchema } from '@auto-engineer/flow';
2
2
 
3
3
  const specVariant1: SpecsSchema = {
4
4
  variant: 'specs',
@@ -14,7 +14,10 @@ const specVariant1: SpecsSchema = {
14
14
  description: 'Handles item creation',
15
15
  client: {
16
16
  description: 'A form that allows users to add items',
17
- specs: ['have fields for id and description'],
17
+ specs: {
18
+ name: 'A form that allows users to add items',
19
+ rules: ['have fields for id and description'],
20
+ },
18
21
  },
19
22
 
20
23
  server: {
@@ -57,7 +60,6 @@ const specVariant1: SpecsSchema = {
57
60
  description: 'Projection of available items',
58
61
  client: {
59
62
  description: 'Show available items',
60
- specs: [],
61
63
  },
62
64
  server: {
63
65
  description: 'Project items based on ItemCreated',
@@ -6,18 +6,18 @@ import { resolve, join } from 'path';
6
6
  import { existsSync } from 'fs';
7
7
  import { generateScaffoldFilePlans, writeScaffoldFilePlans } from '../codegen/scaffoldFromSchema';
8
8
  import { ensureDirExists } from '../codegen/utils/path';
9
- import { SpecsSchemaType } from '@auto-engineer/flow';
9
+ import { Model } from '@auto-engineer/flow';
10
10
  import { fileURLToPath } from 'url';
11
11
  import { dirname } from 'path';
12
12
  import { execa } from 'execa';
13
13
  import createDebug from 'debug';
14
14
  import { defineCommandHandler } from '@auto-engineer/message-bus';
15
15
 
16
- const debug = createDebug('emmett:generate-server');
17
- const debugSchema = createDebug('emmett:generate-server:schema');
18
- const debugFiles = createDebug('emmett:generate-server:files');
19
- const debugDeps = createDebug('emmett:generate-server:deps');
20
- const debugScaffold = createDebug('emmett:generate-server:scaffold');
16
+ const debug = createDebug('auto:generate-server');
17
+ const debugSchema = createDebug('auto:generate-server:schema');
18
+ const debugFiles = createDebug('auto:generate-server:files');
19
+ const debugDeps = createDebug('auto:generate-server:deps');
20
+ const debugScaffold = createDebug('auto:generate-server:scaffold');
21
21
 
22
22
  type DefaultRecord = Record<string, unknown>;
23
23
  export type Command<CommandType extends string = string, CommandData extends DefaultRecord = DefaultRecord> = Readonly<{
@@ -117,12 +117,12 @@ async function validateSchemaFile(
117
117
  return null;
118
118
  }
119
119
 
120
- async function readAndParseSchema(absSchema: string): Promise<SpecsSchemaType> {
120
+ async function readAndParseSchema(absSchema: string): Promise<Model> {
121
121
  debugSchema('Reading schema file from %s', absSchema);
122
122
  const content = await readFile(absSchema, 'utf8');
123
123
 
124
124
  debugSchema('Schema content length: %d bytes', content.length);
125
- const spec = JSON.parse(content) as SpecsSchemaType;
125
+ const spec = JSON.parse(content) as Model;
126
126
 
127
127
  debugSchema('Parsed schema:');
128
128
  debugSchema(' Flows: %d', spec.flows?.length || 0);
@@ -133,7 +133,7 @@ async function readAndParseSchema(absSchema: string): Promise<SpecsSchemaType> {
133
133
  return spec;
134
134
  }
135
135
 
136
- function logFlowDetails(spec: SpecsSchemaType): void {
136
+ function logFlowDetails(spec: Model): void {
137
137
  if (spec.flows !== undefined && spec.flows.length > 0) {
138
138
  debugSchema(
139
139
  'Flow names: %o',
@@ -148,7 +148,7 @@ function logFlowDetails(spec: SpecsSchemaType): void {
148
148
  }
149
149
  }
150
150
 
151
- async function generateAndWriteScaffold(spec: SpecsSchemaType, serverDir: string): Promise<void> {
151
+ async function generateAndWriteScaffold(spec: Model, serverDir: string): Promise<void> {
152
152
  const domainFlowsPath = join(serverDir, 'src', 'domain', 'flows');
153
153
  debugScaffold('Generating scaffold file plans');
154
154
  debugScaffold(' Domain flows path: %s', domainFlowsPath);
@@ -169,7 +169,7 @@ async function generateAndWriteScaffold(spec: SpecsSchemaType, serverDir: string
169
169
  }
170
170
 
171
171
  async function copyAllFiles(serverDir: string): Promise<void> {
172
- const packageRoot = path.resolve(__dirname, '..');
172
+ const packageRoot = path.resolve(__dirname, '../../../src');
173
173
  debugFiles('Package root: %s', packageRoot);
174
174
 
175
175
  debugFiles('Copying utility files...');
@@ -183,16 +183,16 @@ async function copyAllFiles(serverDir: string): Promise<void> {
183
183
  }
184
184
 
185
185
  async function writeConfigurationFiles(serverDir: string, absDest: string): Promise<void> {
186
- debugFiles('Writing package.json...');
186
+ debugFiles(`Writing package.json... to ${serverDir}`);
187
187
  await writePackage(serverDir);
188
188
 
189
- debugFiles('Writing tsconfig.json...');
189
+ debugFiles(`Writing tsconfig.json... to ${serverDir}`);
190
190
  await writeTsconfig(serverDir);
191
191
 
192
- debugFiles('Writing vitest config...');
192
+ debugFiles(`Writing vitest config... to ${serverDir}`);
193
193
  await writeVitestConfig(serverDir);
194
194
 
195
- debugFiles('Generating GraphQL schema script...');
195
+ debugFiles(`Generating GraphQL schema script... ${serverDir} to ${absDest}`);
196
196
  await generateSchemaScript(serverDir, absDest);
197
197
  }
198
198
 
@@ -338,19 +338,20 @@ async function copySharedAndRootFiles(from: string, to: string): Promise<void> {
338
338
  async function writePackage(dest: string): Promise<void> {
339
339
  debugFiles('Writing package.json to %s', dest);
340
340
 
341
- const packageRoot = path.resolve(__dirname, '../..');
341
+ const packageRoot = path.resolve(__dirname, '../../..');
342
342
  const localPkgPath = path.resolve(packageRoot, 'package.json');
343
343
  const rootPkgPath = path.resolve(packageRoot, '../../package.json');
344
344
 
345
+ debugFiles(' package root: %s', packageRoot);
345
346
  debugFiles(' Local package path: %s', localPkgPath);
346
347
  debugFiles(' Root package path: %s', rootPkgPath);
347
348
 
348
- const localPkg = (await fs.readJson(localPkgPath).catch(() => ({}))) as {
349
+ const localPkg = (await fs.readJson(localPkgPath)) as {
349
350
  dependencies?: Record<string, string>;
350
351
  devDependencies?: Record<string, string>;
351
352
  };
352
353
 
353
- const rootPkg = (await fs.readJson(rootPkgPath).catch(() => ({}))) as {
354
+ const rootPkg = (await fs.readJson(rootPkgPath)) as {
354
355
  dependencies?: Record<string, string>;
355
356
  devDependencies?: Record<string, string>;
356
357
  };
@@ -392,6 +393,7 @@ async function writePackage(dest: string): Promise<void> {
392
393
  dependencies: resolveDeps([
393
394
  '@event-driven-io/emmett',
394
395
  'type-graphql',
396
+ 'graphql-type-json',
395
397
  'graphql',
396
398
  'fast-glob',
397
399
  'reflect-metadata',
@@ -402,7 +404,12 @@ async function writePackage(dest: string): Promise<void> {
402
404
  devDependencies: resolveDeps(['typescript', 'vitest', 'tsx']),
403
405
  } as const;
404
406
 
405
- const existingPkg = (await fs.readJson(path.join(dest, 'package.json')).catch(() => ({}))) as Record<string, unknown>;
407
+ debugFiles('Loading package.json from', path.join(dest, 'package.json'));
408
+ const existingPkg = (await fs.readJson(path.join(dest, 'package.json')).catch(() => {
409
+ debugFiles('Failed to load package.json, using empty object');
410
+ return {};
411
+ })) as Record<string, unknown>;
412
+ debugFiles('Existing package.json:', existingPkg);
406
413
  const mergedDeps = {
407
414
  ...(existingPkg.dependencies as Record<string, string>),
408
415
  ...packageJson.dependencies,
@@ -417,6 +424,7 @@ async function writeTsconfig(dest: string): Promise<void> {
417
424
  module: 'ESNext',
418
425
  moduleResolution: 'bundler',
419
426
  strict: true,
427
+ outDir: './dist',
420
428
  skipLibCheck: true,
421
429
  emitDecoratorMetadata: true,
422
430
  experimentalDecorators: true,
@@ -488,29 +496,29 @@ main().catch((err) => {
488
496
  }
489
497
 
490
498
  async function installDependenciesAndGenerateSchema(serverDir: string, workingDir: string): Promise<void> {
491
- debugDeps('📦 Installing dependencies...');
499
+ debugDeps('Installing dependencies...');
492
500
  debugDeps('Starting dependency installation in %s', serverDir);
501
+ debugDeps('Hint: You can debug by manually running: cd server && pnpm install && npx tsx scripts/generate-schema.ts');
493
502
 
494
503
  try {
495
- debugDeps('Running: pnpm install');
496
- await execa('pnpm', ['install'], { cwd: serverDir });
497
- debugDeps('✅ Dependencies installed successfully');
504
+ debugDeps('Running pnpm install');
505
+ await execa('pnpm', ['install', '--ignore-workspace'], { cwd: serverDir });
498
506
  debugDeps('Dependencies installed successfully');
507
+ } catch (error) {
508
+ debugDeps('Failed to pnpm install: %s', error instanceof Error ? error.message : 'Unknown error');
509
+ }
499
510
 
500
- debugDeps('🔄 Generating GraphQL schema...');
501
- debugDeps('Running: tsx scripts/generate-schema.ts');
511
+ try {
512
+ debugDeps('Generating GraphQL schema...');
513
+ debugDeps('Running: tsx scripts/generate-schema.ts', serverDir + '/scripts/generate-schema.ts');
502
514
  await execa('tsx', ['scripts/generate-schema.ts'], { cwd: serverDir });
503
-
504
515
  const schemaPath = join(workingDir, '.context', 'schema.graphql');
505
- debugDeps('✅ GraphQL schema generated at: %s', schemaPath);
506
516
  debugDeps('GraphQL schema generated at: %s', schemaPath);
507
517
  } catch (error) {
508
- debugDeps('Failed to install dependencies or generate schema: %O', error);
509
518
  debugDeps(
510
- '⚠️ Failed to install dependencies or generate schema: %s',
519
+ 'Failed to run tsx scripts/generate-schema.ts: %s',
511
520
  error instanceof Error ? error.message : 'Unknown error',
512
521
  );
513
- debugDeps('You can manually run: cd server && pnpm install && npx tsx scripts/generate-schema.ts');
514
522
  }
515
523
  }
516
524
 
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "composite": false,
5
+ "types": ["vitest/globals", "node"]
6
+ },
7
+ "include": ["src/**/*", "**/*.specs.ts", "**/*.spec.ts", "**/*.test.ts"],
8
+ "exclude": ["node_modules", "dist"]
9
+ }
@@ -1,14 +0,0 @@
1
-
2
- > @auto-engineer/server-generator-apollo-emmett@0.8.5 test /home/runner/work/auto-engineer/auto-engineer/packages/server-generator-apollo-emmett
3
- > vitest run --reporter=dot
4
-
5
-
6
-  RUN  v3.2.4 /home/runner/work/auto-engineer/auto-engineer/packages/server-generator-apollo-emmett
7
-
8
- ···················-···
9
-
10
-  Test Files  15 passed | 1 skipped (16)
11
-  Tests  22 passed | 1 skipped (23)
12
-  Start at  14:02:58
13
-  Duration  6.14s (transform 770ms, setup 0ms, collect 5.05s, tests 5.57s, environment 4ms, prepare 2.21s)
14
-
@@ -1,4 +0,0 @@
1
-
2
- > @auto-engineer/server-generator-apollo-emmett@0.8.5 type-check /home/runner/work/auto-engineer/auto-engineer/packages/server-generator-apollo-emmett
3
- > tsc --noEmit --project tsconfig.json
4
-
@@ -1,124 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { generateScaffoldFilePlans } from '../../scaffoldFromSchema';
3
- import { SpecsSchemaType as SpecsSchema } from '@auto-engineer/flow';
4
-
5
- describe('mutation.resolver.ts.ejs', () => {
6
- it('should generate a valid mutation resolver file', async () => {
7
- const spec: SpecsSchema = {
8
- variant: 'specs',
9
- flows: [
10
- {
11
- name: 'Host creates a listing',
12
- slices: [
13
- {
14
- type: 'command',
15
- name: 'Create listing',
16
- client: {
17
- description: 'A form that allows users to add a new listing',
18
- specs: [],
19
- },
20
- server: {
21
- description: 'Handles listing creation',
22
- specs: {
23
- name: 'Create listing command',
24
- rules: [
25
- {
26
- description: 'Should create listing successfully',
27
- examples: [
28
- {
29
- description: 'User creates listing with valid data',
30
- when: {
31
- commandRef: 'CreateListing',
32
- exampleData: {
33
- propertyId: 'listing_123',
34
- title: 'Modern Downtown Apartment',
35
- pricePerNight: 250,
36
- maxGuests: 4,
37
- amenities: ['wifi', 'kitchen'],
38
- available: true,
39
- tags: ['sea view', 'balcony'],
40
- rating: 4.8,
41
- metadata: { petsAllowed: true },
42
- listedAt: '2024-01-15T10:00:00Z',
43
- },
44
- },
45
- then: [],
46
- },
47
- ],
48
- },
49
- ],
50
- },
51
- },
52
- },
53
- ],
54
- },
55
- ],
56
- messages: [
57
- {
58
- type: 'command',
59
- name: 'CreateListing',
60
- fields: [
61
- { name: 'propertyId', type: 'string', required: true },
62
- { name: 'title', type: 'string', required: true },
63
- { name: 'pricePerNight', type: 'number', required: true },
64
- { name: 'maxGuests', type: 'number', required: true },
65
- { name: 'amenities', type: 'string[]', required: true },
66
- { name: 'available', type: 'boolean', required: true },
67
- { name: 'tags', type: 'string[]', required: true },
68
- { name: 'rating', type: 'number', required: true },
69
- { name: 'metadata', type: 'object', required: true },
70
- { name: 'listedAt', type: 'Date', required: true },
71
- ],
72
- },
73
- ],
74
- };
75
-
76
- const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, undefined, 'src/domain/flows');
77
- const mutationFile = plans.find((p) => p.outputPath.endsWith('mutation.resolver.ts'));
78
-
79
- expect(mutationFile?.contents).toMatchInlineSnapshot(`
80
- "import { Mutation, Resolver, Arg, Ctx, Field, InputType } from 'type-graphql';
81
- import { type GraphQLContext, sendCommand, MutationResponse } from '../../../shared';
82
-
83
- @InputType()
84
- export class CreateListingInput {
85
- @Field(() => String)
86
- propertyId!: string;
87
- @Field(() => String)
88
- title!: string;
89
- @Field(() => Number)
90
- pricePerNight!: number;
91
- @Field(() => Number)
92
- maxGuests!: number;
93
- @Field(() => [String])
94
- amenities!: string[];
95
- @Field(() => Boolean)
96
- available!: boolean;
97
- @Field(() => [String])
98
- tags!: string[];
99
- @Field(() => Number)
100
- rating!: number;
101
- @Field(() => Object)
102
- metadata!: object;
103
- @Field(() => Date)
104
- listedAt!: Date;
105
- }
106
-
107
- @Resolver()
108
- export class CreateListingResolver {
109
- @Mutation(() => MutationResponse)
110
- async createListing(
111
- @Arg('input', () => CreateListingInput) input: CreateListingInput,
112
- @Ctx() ctx: GraphQLContext,
113
- ): Promise<MutationResponse> {
114
- return await sendCommand(ctx.messageBus, {
115
- type: 'CreateListing',
116
- kind: 'Command',
117
- data: { ...input },
118
- });
119
- }
120
- }
121
- "
122
- `);
123
- });
124
- });
@@ -1,25 +0,0 @@
1
- import { Mutation, Resolver, Arg, Ctx, Field, InputType } from 'type-graphql';
2
- import { type GraphQLContext, sendCommand, MutationResponse } from '../../../shared';
3
-
4
- @InputType()
5
- export class <%= pascalCase(commands[0].type) %>Input {
6
- <% for (const field of commands[0].fields) { -%>
7
- @Field(() => <%= graphqlType(field.tsType) %><%= field.required === false ? ', { nullable: true }' : '' %>)
8
- <%= field.name %><%= field.required === false ? '?' : '' %><%= field.required !== false ? '!' : '' %>: <%- field.tsType %>;
9
- <% } -%>
10
- }
11
-
12
- @Resolver()
13
- export class <%= pascalCase(commands[0].type) %>Resolver {
14
- @Mutation(() => MutationResponse)
15
- async <%= camelCase(commands[0].type) %>(
16
- @Arg('input', () => <%= pascalCase(commands[0].type) %>Input) input: <%= pascalCase(commands[0].type) %>Input,
17
- @Ctx() ctx: GraphQLContext
18
- ): Promise<MutationResponse> {
19
- return await sendCommand(ctx.messageBus, {
20
- type: '<%= commands[0].type %>',
21
- kind: 'Command',
22
- data: { ...input },
23
- });
24
- }
25
- }