@auto-engineer/server-generator-apollo-emmett 0.11.9 → 0.11.11

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 (134) hide show
  1. package/.turbo/turbo-build.log +5 -6
  2. package/.turbo/turbo-format.log +1 -1
  3. package/.turbo/turbo-lint.log +1 -1
  4. package/.turbo/turbo-test.log +4 -4
  5. package/.turbo/turbo-type-check.log +1 -1
  6. package/CHANGELOG.md +18 -0
  7. package/dist/src/codegen/extract/commands.d.ts +1 -1
  8. package/dist/src/codegen/extract/commands.d.ts.map +1 -1
  9. package/dist/src/codegen/extract/data-sink.d.ts +1 -1
  10. package/dist/src/codegen/extract/data-sink.d.ts.map +1 -1
  11. package/dist/src/codegen/extract/events.d.ts +1 -1
  12. package/dist/src/codegen/extract/events.d.ts.map +1 -1
  13. package/dist/src/codegen/extract/gwt.d.ts +1 -1
  14. package/dist/src/codegen/extract/gwt.d.ts.map +1 -1
  15. package/dist/src/codegen/extract/index.d.ts +1 -0
  16. package/dist/src/codegen/extract/index.d.ts.map +1 -1
  17. package/dist/src/codegen/extract/index.js +1 -0
  18. package/dist/src/codegen/extract/index.js.map +1 -1
  19. package/dist/src/codegen/extract/messages.d.ts +1 -1
  20. package/dist/src/codegen/extract/messages.d.ts.map +1 -1
  21. package/dist/src/codegen/extract/projection.d.ts +1 -1
  22. package/dist/src/codegen/extract/projection.d.ts.map +1 -1
  23. package/dist/src/codegen/extract/query.d.ts +1 -1
  24. package/dist/src/codegen/extract/query.d.ts.map +1 -1
  25. package/dist/src/codegen/extract/states.d.ts +1 -1
  26. package/dist/src/codegen/extract/states.d.ts.map +1 -1
  27. package/dist/src/codegen/extract/type-helpers.d.ts +13 -0
  28. package/dist/src/codegen/extract/type-helpers.d.ts.map +1 -0
  29. package/dist/src/codegen/extract/type-helpers.js +98 -0
  30. package/dist/src/codegen/extract/type-helpers.js.map +1 -0
  31. package/dist/src/codegen/scaffoldFromSchema.d.ts +3 -3
  32. package/dist/src/codegen/scaffoldFromSchema.d.ts.map +1 -1
  33. package/dist/src/codegen/scaffoldFromSchema.js +202 -19
  34. package/dist/src/codegen/scaffoldFromSchema.js.map +1 -1
  35. package/dist/src/codegen/templates/command/commands.specs.ts +3 -3
  36. package/dist/src/codegen/templates/command/commands.ts.ejs +14 -9
  37. package/dist/src/codegen/templates/command/decide.specs.specs.ts +54 -54
  38. package/dist/src/codegen/templates/command/decide.specs.ts +13 -9
  39. package/dist/src/codegen/templates/command/decide.ts.ejs +1 -0
  40. package/dist/src/codegen/templates/command/events.specs.ts +3 -3
  41. package/dist/src/codegen/templates/command/events.ts.ejs +16 -13
  42. package/dist/src/codegen/templates/command/evolve.specs.ts +3 -3
  43. package/dist/src/codegen/templates/command/handle.specs.ts +10 -5
  44. package/dist/src/codegen/templates/command/mutation.resolver.specs.ts +8 -7
  45. package/dist/src/codegen/templates/command/mutation.resolver.ts.ejs +2 -23
  46. package/dist/src/codegen/templates/command/register.specs.ts +4 -4
  47. package/dist/src/codegen/templates/command/register.ts.ejs +1 -4
  48. package/dist/src/codegen/templates/command/state.specs.ts +54 -50
  49. package/dist/src/codegen/templates/command/state.ts.ejs +8 -4
  50. package/dist/src/codegen/templates/query/projection.specs.specs.ts +7 -7
  51. package/dist/src/codegen/templates/query/projection.specs.ts +24 -7
  52. package/dist/src/codegen/templates/query/projection.ts.ejs +64 -12
  53. package/dist/src/codegen/templates/query/query.resolver.specs.ts +19 -16
  54. package/dist/src/codegen/templates/query/query.resolver.ts.ejs +11 -49
  55. package/dist/src/codegen/templates/query/state.specs.ts +3 -3
  56. package/dist/src/codegen/templates/react/react.specs.specs.ts +3 -3
  57. package/dist/src/codegen/templates/react/react.specs.ts +3 -3
  58. package/dist/src/codegen/templates/react/react.ts.ejs +0 -1
  59. package/dist/src/codegen/templates/react/register.specs.ts +3 -3
  60. package/dist/src/codegen/templates/react/register.ts.ejs +0 -1
  61. package/dist/src/codegen/test-data/specVariant1.d.ts +1 -1
  62. package/dist/src/codegen/test-data/specVariant1.d.ts.map +1 -1
  63. package/dist/src/codegen/test-data/specVariant1.js +1 -1
  64. package/dist/src/codegen/test-data/specVariant1.js.map +1 -1
  65. package/dist/src/codegen/types.d.ts +1 -1
  66. package/dist/src/codegen/types.d.ts.map +1 -1
  67. package/dist/src/commands/generate-server.d.ts +0 -1
  68. package/dist/src/commands/generate-server.d.ts.map +1 -1
  69. package/dist/src/commands/generate-server.js +53 -31
  70. package/dist/src/commands/generate-server.js.map +1 -1
  71. package/dist/src/domain/shared/graphql-types.d.ts +10 -0
  72. package/dist/src/domain/shared/graphql-types.d.ts.map +1 -0
  73. package/dist/src/domain/shared/graphql-types.js +40 -0
  74. package/dist/src/domain/shared/graphql-types.js.map +1 -0
  75. package/dist/src/domain/shared/graphql-types.ts +20 -0
  76. package/dist/src/domain/shared/index.d.ts +1 -0
  77. package/dist/src/domain/shared/index.d.ts.map +1 -1
  78. package/dist/src/domain/shared/index.js +1 -0
  79. package/dist/src/domain/shared/index.js.map +1 -1
  80. package/dist/src/domain/shared/index.ts +1 -0
  81. package/dist/src/domain/shared/sendCommand.d.ts +1 -1
  82. package/dist/src/domain/shared/sendCommand.d.ts.map +1 -1
  83. package/dist/src/domain/shared/sendCommand.ts +1 -1
  84. package/dist/src/domain/shared/types.d.ts +5 -7
  85. package/dist/src/domain/shared/types.d.ts.map +1 -1
  86. package/dist/src/domain/shared/types.js +11 -38
  87. package/dist/src/domain/shared/types.js.map +1 -1
  88. package/dist/src/domain/shared/types.ts +10 -16
  89. package/dist/tsconfig.tsbuildinfo +1 -1
  90. package/package.json +4 -4
  91. package/src/codegen/extract/commands.ts +1 -1
  92. package/src/codegen/extract/data-sink.ts +1 -1
  93. package/src/codegen/extract/events.ts +1 -1
  94. package/src/codegen/extract/gwt.ts +1 -1
  95. package/src/codegen/extract/index.ts +1 -0
  96. package/src/codegen/extract/messages.ts +1 -1
  97. package/src/codegen/extract/projection.ts +1 -1
  98. package/src/codegen/extract/query.ts +1 -1
  99. package/src/codegen/extract/states.ts +1 -1
  100. package/src/codegen/extract/type-helpers.ts +102 -0
  101. package/src/codegen/scaffoldFromSchema.ts +283 -25
  102. package/src/codegen/templates/command/commands.specs.ts +3 -3
  103. package/src/codegen/templates/command/commands.ts.ejs +14 -9
  104. package/src/codegen/templates/command/decide.specs.specs.ts +54 -54
  105. package/src/codegen/templates/command/decide.specs.ts +13 -9
  106. package/src/codegen/templates/command/decide.ts.ejs +1 -0
  107. package/src/codegen/templates/command/events.specs.ts +3 -3
  108. package/src/codegen/templates/command/events.ts.ejs +16 -13
  109. package/src/codegen/templates/command/evolve.specs.ts +3 -3
  110. package/src/codegen/templates/command/handle.specs.ts +10 -5
  111. package/src/codegen/templates/command/mutation.resolver.specs.ts +8 -7
  112. package/src/codegen/templates/command/mutation.resolver.ts.ejs +2 -23
  113. package/src/codegen/templates/command/register.specs.ts +4 -4
  114. package/src/codegen/templates/command/register.ts.ejs +1 -4
  115. package/src/codegen/templates/command/state.specs.ts +54 -50
  116. package/src/codegen/templates/command/state.ts.ejs +8 -4
  117. package/src/codegen/templates/query/projection.specs.specs.ts +7 -7
  118. package/src/codegen/templates/query/projection.specs.ts +24 -7
  119. package/src/codegen/templates/query/projection.ts.ejs +64 -12
  120. package/src/codegen/templates/query/query.resolver.specs.ts +19 -16
  121. package/src/codegen/templates/query/query.resolver.ts.ejs +11 -49
  122. package/src/codegen/templates/query/state.specs.ts +3 -3
  123. package/src/codegen/templates/react/react.specs.specs.ts +3 -3
  124. package/src/codegen/templates/react/react.specs.ts +3 -3
  125. package/src/codegen/templates/react/react.ts.ejs +0 -1
  126. package/src/codegen/templates/react/register.specs.ts +3 -3
  127. package/src/codegen/templates/react/register.ts.ejs +0 -1
  128. package/src/codegen/test-data/specVariant1.ts +2 -2
  129. package/src/codegen/types.ts +1 -1
  130. package/src/commands/generate-server.ts +63 -34
  131. package/src/domain/shared/graphql-types.ts +20 -0
  132. package/src/domain/shared/index.ts +1 -0
  133. package/src/domain/shared/sendCommand.ts +1 -1
  134. package/src/domain/shared/types.ts +10 -16
@@ -1,12 +1,12 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { Model as SpecsSchema } from '@auto-engineer/flow';
2
+ import { Model as SpecsSchema } from '@auto-engineer/narrative';
3
3
  import { generateScaffoldFilePlans } from '../../scaffoldFromSchema';
4
4
 
5
5
  describe('decide.ts.ejs', () => {
6
6
  it('should generate a valid decide file when both command and event exist', async () => {
7
7
  const spec: SpecsSchema = {
8
8
  variant: 'specs',
9
- flows: [
9
+ narratives: [
10
10
  {
11
11
  name: 'Host creates a listing',
12
12
  slices: [
@@ -72,7 +72,7 @@ describe('decide.ts.ejs', () => {
72
72
  ],
73
73
  };
74
74
 
75
- const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, undefined, 'src/domain/flows');
75
+ const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
76
76
  const decideFile = plans.find((p) => p.outputPath.endsWith('decide.ts'));
77
77
 
78
78
  expect(decideFile?.contents).toMatchInlineSnapshot(`
@@ -93,6 +93,7 @@ describe('decide.ts.ejs', () => {
93
93
  * - Validate the command input fields
94
94
  * - Inspect the current domain \`state\` to determine if the command is allowed
95
95
  * - If invalid, throw one of the following domain errors: \`NotFoundError\`, \`ValidationError\`, or \`IllegalStateError\`
96
+ * ⚠️ Error constructors: NotFoundError takes { id, type, message? }, while IllegalStateError/ValidationError take string
96
97
  * - If valid, return one or more events with the correct structure
97
98
  *
98
99
  * ⚠️ Only read from inputs — never mutate them. \`evolve.ts\` handles state updates.
@@ -116,7 +117,7 @@ describe('decide.ts.ejs', () => {
116
117
  it('should include implementation instructions when prior events are required', async () => {
117
118
  const spec: SpecsSchema = {
118
119
  variant: 'specs',
119
- flows: [
120
+ narratives: [
120
121
  {
121
122
  name: 'Host removes a listing',
122
123
  slices: [
@@ -197,7 +198,7 @@ describe('decide.ts.ejs', () => {
197
198
  ],
198
199
  };
199
200
 
200
- const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, undefined, 'src/domain/flows');
201
+ const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
201
202
  const decideFile = plans.find((p) => p.outputPath.endsWith('decide.ts'));
202
203
 
203
204
  expect(decideFile?.contents).toMatchInlineSnapshot(`
@@ -218,6 +219,7 @@ describe('decide.ts.ejs', () => {
218
219
  * - Validate the command input fields
219
220
  * - Inspect the current domain \`state\` to determine if the command is allowed
220
221
  * - If invalid, throw one of the following domain errors: \`NotFoundError\`, \`ValidationError\`, or \`IllegalStateError\`
222
+ * ⚠️ Error constructors: NotFoundError takes { id, type, message? }, while IllegalStateError/ValidationError take string
221
223
  * - If valid, return one or more events with the correct structure
222
224
  *
223
225
  * ⚠️ Only read from inputs — never mutate them. \`evolve.ts\` handles state updates.
@@ -241,7 +243,7 @@ describe('decide.ts.ejs', () => {
241
243
  it('should generate a decide file that handles multiple GWTs including an error', async () => {
242
244
  const spec: SpecsSchema = {
243
245
  variant: 'specs',
244
- flows: [
246
+ narratives: [
245
247
  {
246
248
  name: 'Host creates a listing',
247
249
  slices: [
@@ -337,7 +339,7 @@ describe('decide.ts.ejs', () => {
337
339
  ],
338
340
  };
339
341
 
340
- const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, undefined, 'src/domain/flows');
342
+ const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
341
343
  const decideFile = plans.find((p) => p.outputPath.endsWith('decide.ts'));
342
344
 
343
345
  expect(decideFile?.contents).toMatchInlineSnapshot(`
@@ -358,6 +360,7 @@ describe('decide.ts.ejs', () => {
358
360
  * - Validate the command input fields
359
361
  * - Inspect the current domain \`state\` to determine if the command is allowed
360
362
  * - If invalid, throw one of the following domain errors: \`NotFoundError\`, \`ValidationError\`, or \`IllegalStateError\`
363
+ * ⚠️ Error constructors: NotFoundError takes { id, type, message? }, while IllegalStateError/ValidationError take string
361
364
  * - If valid, return one or more events with the correct structure
362
365
  *
363
366
  * ⚠️ Only read from inputs — never mutate them. \`evolve.ts\` handles state updates.
@@ -385,7 +388,7 @@ describe('decide.ts.ejs', () => {
385
388
  it('should include integration return type and usage in decide function', async () => {
386
389
  const spec: SpecsSchema = {
387
390
  variant: 'specs',
388
- flows: [
391
+ narratives: [
389
392
  {
390
393
  name: 'Assistant suggests items',
391
394
  slices: [
@@ -522,7 +525,7 @@ describe('decide.ts.ejs', () => {
522
525
  ],
523
526
  };
524
527
 
525
- const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, spec.integrations);
528
+ const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, spec.integrations);
526
529
  const decideFile = plans.find((p) => p.outputPath.endsWith('decide.ts'));
527
530
 
528
531
  expect(decideFile?.contents).toMatchInlineSnapshot(`
@@ -545,6 +548,7 @@ describe('decide.ts.ejs', () => {
545
548
  * - Inspect the current domain \`state\` to determine if the command is allowed
546
549
  * - Use \`products\` (integration result) to enrich or filter the output
547
550
  * - If invalid, throw one of the following domain errors: \`NotFoundError\`, \`ValidationError\`, or \`IllegalStateError\`
551
+ * ⚠️ Error constructors: NotFoundError takes { id, type, message? }, while IllegalStateError/ValidationError take string
548
552
  * - If valid, return one or more events with the correct structure
549
553
  *
550
554
  * ⚠️ Only read from inputs — never mutate them. \`evolve.ts\` handles state updates.
@@ -71,6 +71,7 @@ case '<%= command %>': {
71
71
  * - Use `<%= camelCase(integrationReturnType) %>` (integration result) to enrich or filter the output
72
72
  <% } -%>
73
73
  * - If invalid, throw one of the following domain errors: `NotFoundError`, `ValidationError`, or `IllegalStateError`
74
+ * ⚠️ Error constructors: NotFoundError takes { id, type, message? }, while IllegalStateError/ValidationError take string
74
75
  * - If valid, return one or more events with the correct structure
75
76
  *
76
77
  * ⚠️ Only read from inputs — never mutate them. `evolve.ts` handles state updates.
@@ -1,12 +1,12 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { Model as SpecsSchema } from '@auto-engineer/flow';
2
+ import { Model as SpecsSchema } from '@auto-engineer/narrative';
3
3
  import { generateScaffoldFilePlans } from '../../scaffoldFromSchema';
4
4
 
5
5
  describe('events.ts.ejs', () => {
6
6
  it('should generate an event file', async () => {
7
7
  const spec: SpecsSchema = {
8
8
  variant: 'specs',
9
- flows: [
9
+ narratives: [
10
10
  {
11
11
  name: 'Host creates a listing',
12
12
  slices: [
@@ -93,7 +93,7 @@ describe('events.ts.ejs', () => {
93
93
  ],
94
94
  };
95
95
 
96
- const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, undefined, 'src/domain/flows');
96
+ const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
97
97
  const eventFile = plans.find((p) => p.outputPath.endsWith('events.ts'));
98
98
 
99
99
  expect(eventFile?.contents).toMatchInlineSnapshot(`
@@ -1,14 +1,17 @@
1
- <% if (localEvents.length) { -%>
2
- import type { Event } from '@event-driven-io/emmett';
3
-
4
- <% for (const event of localEvents) { -%>
5
- export type <%= pascalCase(event.type) %> = Event<
6
- '<%= event.type %>',
7
- {
8
- <% for (const field of event.fields) { -%>
9
- <%- field.name %>: <%- field.tsType %>;
10
- <% } -%>
11
- }
12
- >;
13
- <% } -%>
1
+ <%
2
+ const enumList = collectEnumNames(localEvents.flatMap(e => e.fields));
3
+ %><% if (localEvents.length) { -%>
4
+ import type { Event } from '@event-driven-io/emmett';
5
+ <% if (enumList.length > 0) { %>import { <%= enumList.join(', ') %> } from '../../../shared';
6
+ <% } %>
7
+ <% for (const event of localEvents) { -%>
8
+ export type <%= pascalCase(event.type) %> = Event<
9
+ '<%= event.type %>',
10
+ {
11
+ <% for (const field of event.fields) { -%>
12
+ <%- field.name %>: <%- toTsFieldType(field.tsType) %>;
13
+ <% } -%>
14
+ }
15
+ >;
16
+ <% } -%>
14
17
  <% } -%>
@@ -1,12 +1,12 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { Model as SpecsSchema } from '@auto-engineer/flow';
2
+ import { Model as SpecsSchema } from '@auto-engineer/narrative';
3
3
  import { generateScaffoldFilePlans } from '../../scaffoldFromSchema';
4
4
 
5
5
  describe('evolve.ts.ejs', () => {
6
6
  it('should generate a valid evolve file from event structure', async () => {
7
7
  const spec: SpecsSchema = {
8
8
  variant: 'specs',
9
- flows: [
9
+ narratives: [
10
10
  {
11
11
  name: 'Host creates a listing',
12
12
  slices: [
@@ -72,7 +72,7 @@ describe('evolve.ts.ejs', () => {
72
72
  ],
73
73
  };
74
74
 
75
- const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, undefined, 'src/domain/flows');
75
+ const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
76
76
  const evolveFile = plans.find((p) => p.outputPath.endsWith('evolve.ts'));
77
77
 
78
78
  expect(evolveFile?.contents).toMatchInlineSnapshot(`
@@ -1,12 +1,12 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { generateScaffoldFilePlans } from '../../scaffoldFromSchema';
3
- import { Model as SpecsSchema } from '@auto-engineer/flow';
3
+ import { Model as SpecsSchema } from '@auto-engineer/narrative';
4
4
 
5
5
  describe('generateScaffoldFilePlans', () => {
6
6
  it('should generate a valid handle file', async () => {
7
7
  const spec: SpecsSchema = {
8
8
  variant: 'specs',
9
- flows: [
9
+ narratives: [
10
10
  {
11
11
  name: 'Host creates a listing',
12
12
  slices: [
@@ -97,7 +97,7 @@ describe('generateScaffoldFilePlans', () => {
97
97
  ],
98
98
  };
99
99
 
100
- const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, undefined, 'src/domain/flows');
100
+ const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
101
101
  const handleFile = plans.find((p) => p.outputPath.endsWith('handle.ts'));
102
102
 
103
103
  expect(handleFile?.contents).toMatchInlineSnapshot(`
@@ -131,7 +131,7 @@ describe('generateScaffoldFilePlans', () => {
131
131
  it('should generate a valid handle file with integration', async () => {
132
132
  const spec: SpecsSchema = {
133
133
  variant: 'specs',
134
- flows: [
134
+ narratives: [
135
135
  {
136
136
  name: 'Assistant suggests items',
137
137
  slices: [
@@ -268,7 +268,12 @@ describe('generateScaffoldFilePlans', () => {
268
268
  ],
269
269
  };
270
270
 
271
- const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, spec.integrations, 'src/domain/flows');
271
+ const plans = await generateScaffoldFilePlans(
272
+ spec.narratives,
273
+ spec.messages,
274
+ spec.integrations,
275
+ 'src/domain/flows',
276
+ );
272
277
  const handleFile = plans.find((p) => p.outputPath.endsWith('handle.ts'));
273
278
 
274
279
  expect(handleFile?.contents).toMatchInlineSnapshot(`
@@ -1,12 +1,12 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { generateScaffoldFilePlans } from '../../scaffoldFromSchema';
3
- import { Model as SpecsSchema } from '@auto-engineer/flow';
3
+ import { Model as SpecsSchema } from '@auto-engineer/narrative';
4
4
 
5
5
  describe('mutation.resolver.ts.ejs', () => {
6
6
  it('should generate a valid mutation resolver file', async () => {
7
7
  const spec: SpecsSchema = {
8
8
  variant: 'specs',
9
- flows: [
9
+ narratives: [
10
10
  {
11
11
  name: 'Host creates a listing',
12
12
  slices: [
@@ -72,7 +72,7 @@ describe('mutation.resolver.ts.ejs', () => {
72
72
  ],
73
73
  };
74
74
 
75
- const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, undefined, 'src/domain/flows');
75
+ const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
76
76
  const mutationFile = plans.find((p) => p.outputPath.endsWith('mutation.resolver.ts'));
77
77
 
78
78
  expect(mutationFile?.contents).toMatchInlineSnapshot(`
@@ -134,7 +134,7 @@ describe('mutation.resolver.ts.ejs', () => {
134
134
  it('should generate the mutation resolver for AnswerQuestion', async () => {
135
135
  const spec: SpecsSchema = {
136
136
  variant: 'specs',
137
- flows: [
137
+ narratives: [
138
138
  {
139
139
  name: 'Questionnaires',
140
140
  slices: [
@@ -220,7 +220,7 @@ describe('mutation.resolver.ts.ejs', () => {
220
220
  integrations: [],
221
221
  };
222
222
 
223
- const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, undefined, 'src/domain/flows');
223
+ const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
224
224
  const mutationFile = plans.find(
225
225
  (p) =>
226
226
  p.outputPath.endsWith('mutation.resolver.ts') && p.contents.includes('export class AnswerQuestionResolver'),
@@ -267,7 +267,7 @@ describe('mutation.resolver.ts.ejs', () => {
267
267
  it('generates nested input types for inline object arrays in a mutation', async () => {
268
268
  const spec: SpecsSchema = {
269
269
  variant: 'specs',
270
- flows: [
270
+ narratives: [
271
271
  {
272
272
  name: 'Cart',
273
273
  slices: [
@@ -315,7 +315,7 @@ describe('mutation.resolver.ts.ejs', () => {
315
315
  ],
316
316
  };
317
317
 
318
- const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, undefined, 'src/domain/flows');
318
+ const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
319
319
  const mutationFile = plans.find(
320
320
  (p) =>
321
321
  p.outputPath.endsWith('mutation.resolver.ts') && p.contents.includes('export class AddItemsToCartResolver'),
@@ -323,6 +323,7 @@ describe('mutation.resolver.ts.ejs', () => {
323
323
 
324
324
  expect(mutationFile?.contents).toMatchInlineSnapshot(`
325
325
  "import { Mutation, Resolver, Arg, Ctx, Field, InputType } from 'type-graphql';
326
+ import { GraphQLJSON } from 'graphql-type-json';
326
327
  import { type GraphQLContext, sendCommand, MutationResponse } from '../../../shared';
327
328
 
328
329
  @InputType()
@@ -1,29 +1,8 @@
1
1
  <%
2
- function isInlineObject(ts) {
3
- return /^\{[\s\S]*\}$/.test((ts ?? '').trim());
4
- }
5
- function isInlineObjectArray(ts) {
6
- const t = (ts ?? '').trim();
7
- return /^Array<\{[\s\S]*\}>$/.test(t) || /^\{[\s\S]*\}\[\]$/.test(t);
8
- }
9
- function baseTs(ts) {
10
- return (ts ?? 'string').replace(/\s*\|\s*null\b/g, '').trim();
11
- }
12
- function fieldUsesDate(ts) {
13
- const b = baseTs(ts);
14
- if (b === 'Date') return true;
15
- if (isInlineObject(b) || isInlineObjectArray(b)) return /:\s*Date\b/.test(b);
16
- return false;
17
- }
18
- function fieldUsesJSON(ts) {
19
- const b = baseTs(ts);
20
- if (b === 'unknown' || b === 'any' || b === 'object') return true;
21
- if (isInlineObject(b) || isInlineObjectArray(b)) return /:\s*(unknown|any|object)\b/.test(b);
22
- return false;
23
- }
24
2
  const cmd = commands[0];
25
3
  const usesDate = cmd.fields.some(f => fieldUsesDate(f.tsType));
26
4
  const usesJSON = cmd.fields.some(f => fieldUsesJSON(f.tsType));
5
+ const enumList = collectEnumNames(cmd.fields);
27
6
 
28
7
  const embeddedInputs = [];
29
8
  for (const f of cmd.fields) {
@@ -38,7 +17,7 @@ for (const f of cmd.fields) {
38
17
  %>
39
18
  import { Mutation, Resolver, Arg, Ctx, Field, InputType<% if (usesDate) { %>, GraphQLISODateTime<% } %> } from 'type-graphql';
40
19
  <% if (usesJSON) { %>import { GraphQLJSON } from 'graphql-type-json';
41
- <% } %>import { type GraphQLContext, sendCommand, MutationResponse } from '../../../shared';
20
+ <% } %>import { type GraphQLContext, sendCommand, MutationResponse<% if (enumList.length > 0) { %>, <%= enumList.join(', ') %><% } %> } from '../../../shared';
42
21
 
43
22
  <% for (const { typeName, tsType } of embeddedInputs) {
44
23
  const inner = tsType.trim().startsWith('Array<')
@@ -1,12 +1,12 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { generateScaffoldFilePlans } from '../../scaffoldFromSchema';
3
- import { Model as SpecsSchema } from '@auto-engineer/flow';
3
+ import { Model as SpecsSchema } from '@auto-engineer/narrative';
4
4
 
5
5
  describe('generateScaffoldFilePlans', () => {
6
6
  it('should generate a valid register file', async () => {
7
7
  const spec: SpecsSchema = {
8
8
  variant: 'specs',
9
- flows: [
9
+ narratives: [
10
10
  {
11
11
  name: 'Host creates a listing',
12
12
  slices: [
@@ -98,7 +98,7 @@ describe('generateScaffoldFilePlans', () => {
98
98
  ],
99
99
  };
100
100
 
101
- const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, undefined, 'src/domain/flows');
101
+ const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
102
102
  const registerFile = plans.find((p) => p.outputPath.endsWith('register.ts'));
103
103
 
104
104
  expect(registerFile?.contents).toMatchInlineSnapshot(`
@@ -107,7 +107,7 @@ describe('generateScaffoldFilePlans', () => {
107
107
  import type { CreateListing } from './commands';
108
108
 
109
109
  export function register(messageBus: CommandProcessor, eventStore: EventStore) {
110
- messageBus.handle((command: CreateListing) => handle(eventStore, command), 'CreateListing');
110
+ messageBus.handle<CreateListing>((command: CreateListing) => handle(eventStore, command), 'CreateListing');
111
111
  }
112
112
  "
113
113
  `);
@@ -4,9 +4,6 @@ import type { <%= commands.map(c => pascalCase(c.type)).join(', ') %> } from './
4
4
 
5
5
  export function register(messageBus: CommandProcessor, eventStore: EventStore) {
6
6
  <% for (const command of commands) { -%>
7
- messageBus.handle(
8
- (command: <%= pascalCase(command.type) %>) => handle(eventStore, command),
9
- '<%= command.type %>'
10
- );
7
+ messageBus.handle<<%= pascalCase(command.type) %>>((command: <%= pascalCase(command.type) %>) => handle(eventStore, command), '<%= command.type %>');
11
8
  <% } -%>
12
9
  }
@@ -1,12 +1,12 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { generateScaffoldFilePlans } from '../../scaffoldFromSchema';
3
- import { Model as SpecsSchema } from '@auto-engineer/flow';
3
+ import { Model as SpecsSchema } from '@auto-engineer/narrative';
4
4
 
5
5
  describe('state.ts.ejs', () => {
6
6
  it('should generate an initial state', async () => {
7
7
  const spec: SpecsSchema = {
8
8
  variant: 'specs',
9
- flows: [
9
+ narratives: [
10
10
  {
11
11
  name: 'Host creates a listing',
12
12
  slices: [
@@ -78,58 +78,62 @@ describe('state.ts.ejs', () => {
78
78
  ],
79
79
  };
80
80
 
81
- const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, undefined, 'src/domain/flows');
81
+ const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
82
82
  const stateFile = plans.find((p) => p.outputPath.endsWith('state.ts'));
83
83
 
84
84
  expect(stateFile?.contents).toMatchInlineSnapshot(`
85
- "/**
86
- * ## IMPLEMENTATION INSTRUCTIONS ##
87
- *
88
- * Define the shape of the domain state for the current slice below. This state is used by \`decide.ts\`
89
- * to determine whether a command is valid.
90
- *
91
- * The state is evolved over time by applying domain events (in \`evolve.ts\`).
92
- * Each event updates the state incrementally based on business rules.
93
- *
94
- * Guidelines:
95
- * - Include only fields that are **read** during command validation.
96
- * - Use discriminated unions (e.g., \`status: 'Pending' | 'Done'\`) to model state transitions.
97
- * - Prefer primitive types: \`string\`, \`boolean\`, \`number\`.
98
- * - Use objects or maps only when structure is essential for decision logic.
99
- *
100
- * Do NOT include:
101
- * - Redundant data already emitted in events unless required to enforce business rules.
102
- * - Fields used only for projections, UI, or query purposes.
103
- *
104
- * ### Example (for a Task domain):
105
- *
106
- * \`\`\`ts
107
- * export type PendingTask = {
108
- * status: 'Pending';
109
- * };
110
- *
111
- * export type InProgressTask = {
112
- * status: 'InProgress';
113
- * startedAt: string;
114
- * };
115
- *
116
- * export type CompletedTask = {
117
- * status: 'Completed';
118
- * completedAt: string;
119
- * };
120
- *
121
- * export type State = PendingTask | InProgressTask | CompletedTask;
122
- * \`\`\`
123
- */
85
+ "/**
86
+ * ## IMPLEMENTATION INSTRUCTIONS ##
87
+ *
88
+ * Define the shape of the domain state for the current slice below. This state is used by \`decide.ts\`
89
+ * to determine whether a command is valid.
90
+ *
91
+ * The state is evolved over time by applying domain events (in \`evolve.ts\`).
92
+ * Each event updates the state incrementally based on business rules.
93
+ *
94
+ * Guidelines:
95
+ * - Include only fields that are **read** during command validation.
96
+ * - Use discriminated unions with string literal types (e.g., \`status: 'pending' | 'done'\`) to model state transitions.
97
+ * - IMPORTANT: If an enum exists in domain/shared/types.ts for your field (e.g., Status enum), use the enum constant type instead (e.g., \`status: Status.PENDING\`).
98
+ * - Prefer primitive types: \`string\`, \`boolean\`, \`number\`.
99
+ * - Use objects or maps only when structure is essential for decision logic.
100
+ *
101
+ * Do NOT include:
102
+ * - Redundant data already emitted in events unless required to enforce business rules.
103
+ * - Fields used only for projections, UI, or query purposes.
104
+ *
105
+ * ### Example (for a Task domain):
106
+ *
107
+ * \`\`\`ts
108
+ * export type PendingTask = {
109
+ * status: 'pending';
110
+ * };
111
+ *
112
+ * export type InProgressTask = {
113
+ * status: 'in_progress';
114
+ * startedAt: string;
115
+ * };
116
+ *
117
+ * export type CompletedTask = {
118
+ * status: 'completed';
119
+ * completedAt: string;
120
+ * };
121
+ *
122
+ * export type State = PendingTask | InProgressTask | CompletedTask;
123
+ * \`\`\`
124
+ *
125
+ * Note: Status string literals should match your schema's enum values (usually snake_case).
126
+ * If an enum is defined in domain/shared/types.ts, reference the enum type instead of literals.
127
+ */
124
128
 
125
- // TODO: Replace with a discriminated union of domain states for the current slice
126
- export type State = {};
129
+ // TODO: Replace with a discriminated union of domain states for the current slice
130
+ export type State = {};
127
131
 
128
- // TODO: Replace the Return with the initial domain state of the current slice
129
- export const initialState = (): State => {
130
- return {};
131
- };
132
- "
133
- `);
132
+ // TODO: Replace the Return with the initial domain state of the current slice
133
+ export const initialState = (): State => {
134
+ return {};
135
+ };
136
+ "
137
+ `);
134
138
  });
135
139
  });
@@ -9,7 +9,8 @@
9
9
  *
10
10
  * Guidelines:
11
11
  * - Include only fields that are **read** during command validation.
12
- * - Use discriminated unions (e.g., `status: 'Pending' | 'Done'`) to model state transitions.
12
+ * - Use discriminated unions with string literal types (e.g., `status: 'pending' | 'done'`) to model state transitions.
13
+ * - IMPORTANT: If an enum exists in domain/shared/types.ts for your field (e.g., Status enum), use the enum constant type instead (e.g., `status: Status.PENDING`).
13
14
  * - Prefer primitive types: `string`, `boolean`, `number`.
14
15
  * - Use objects or maps only when structure is essential for decision logic.
15
16
  *
@@ -21,21 +22,24 @@
21
22
  *
22
23
  * ```ts
23
24
  * export type PendingTask = {
24
- * status: 'Pending';
25
+ * status: 'pending';
25
26
  * };
26
27
  *
27
28
  * export type InProgressTask = {
28
- * status: 'InProgress';
29
+ * status: 'in_progress';
29
30
  * startedAt: string;
30
31
  * };
31
32
  *
32
33
  * export type CompletedTask = {
33
- * status: 'Completed';
34
+ * status: 'completed';
34
35
  * completedAt: string;
35
36
  * };
36
37
  *
37
38
  * export type State = PendingTask | InProgressTask | CompletedTask;
38
39
  * ```
40
+ *
41
+ * Note: Status string literals should match your schema's enum values (usually snake_case).
42
+ * If an enum is defined in domain/shared/types.ts, reference the enum type instead of literals.
39
43
  */
40
44
 
41
45
  // TODO: Replace with a discriminated union of domain states for the current slice
@@ -1,12 +1,12 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { generateScaffoldFilePlans } from '../../scaffoldFromSchema';
3
- import { Model as SpecsSchema } from '@auto-engineer/flow';
3
+ import { Model as SpecsSchema } from '@auto-engineer/narrative';
4
4
 
5
5
  describe('projection.specs.ts.ejs', () => {
6
6
  it('should generate a valid test spec for a query slice projection', async () => {
7
7
  const spec: SpecsSchema = {
8
8
  variant: 'specs',
9
- flows: [
9
+ narratives: [
10
10
  {
11
11
  name: 'listing-flow',
12
12
  slices: [
@@ -180,7 +180,7 @@ describe('projection.specs.ts.ejs', () => {
180
180
  ],
181
181
  } as SpecsSchema;
182
182
 
183
- const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, undefined, 'src/domain/flows');
183
+ const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
184
184
  const specFile = plans.find((p) => p.outputPath.endsWith('projection.specs.ts'));
185
185
 
186
186
  expect(specFile?.contents).toMatchInlineSnapshot(`
@@ -241,7 +241,7 @@ describe('projection.specs.ts.ejs', () => {
241
241
  it('should generate a valid test spec for a model with given/when/then pattern', async () => {
242
242
  const questionnaireSpec: SpecsSchema = {
243
243
  variant: 'specs',
244
- flows: [
244
+ narratives: [
245
245
  {
246
246
  name: 'Questionnaires',
247
247
  slices: [
@@ -363,7 +363,7 @@ describe('projection.specs.ts.ejs', () => {
363
363
  } as SpecsSchema;
364
364
 
365
365
  const plans = await generateScaffoldFilePlans(
366
- questionnaireSpec.flows,
366
+ questionnaireSpec.narratives,
367
367
  questionnaireSpec.messages,
368
368
  undefined,
369
369
  'src/domain/flows',
@@ -380,7 +380,7 @@ describe('projection.specs.ts.ejs', () => {
380
380
  it('should include all events from both given and when clauses in projection imports and types', async () => {
381
381
  const spec: SpecsSchema = {
382
382
  variant: 'specs',
383
- flows: [
383
+ narratives: [
384
384
  {
385
385
  name: 'questionnaires',
386
386
  slices: [
@@ -569,7 +569,7 @@ describe('projection.specs.ts.ejs', () => {
569
569
  } as SpecsSchema;
570
570
 
571
571
  const plans = await generateScaffoldFilePlans(
572
- spec.flows,
572
+ spec.narratives,
573
573
  [
574
574
  {
575
575
  type: 'command',