@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.
- package/.turbo/turbo-build.log +5 -6
- package/.turbo/turbo-format.log +1 -1
- package/.turbo/turbo-lint.log +1 -1
- package/.turbo/turbo-test.log +4 -4
- package/.turbo/turbo-type-check.log +1 -1
- package/CHANGELOG.md +18 -0
- package/dist/src/codegen/extract/commands.d.ts +1 -1
- package/dist/src/codegen/extract/commands.d.ts.map +1 -1
- package/dist/src/codegen/extract/data-sink.d.ts +1 -1
- package/dist/src/codegen/extract/data-sink.d.ts.map +1 -1
- package/dist/src/codegen/extract/events.d.ts +1 -1
- package/dist/src/codegen/extract/events.d.ts.map +1 -1
- package/dist/src/codegen/extract/gwt.d.ts +1 -1
- package/dist/src/codegen/extract/gwt.d.ts.map +1 -1
- package/dist/src/codegen/extract/index.d.ts +1 -0
- package/dist/src/codegen/extract/index.d.ts.map +1 -1
- package/dist/src/codegen/extract/index.js +1 -0
- package/dist/src/codegen/extract/index.js.map +1 -1
- package/dist/src/codegen/extract/messages.d.ts +1 -1
- package/dist/src/codegen/extract/messages.d.ts.map +1 -1
- package/dist/src/codegen/extract/projection.d.ts +1 -1
- package/dist/src/codegen/extract/projection.d.ts.map +1 -1
- package/dist/src/codegen/extract/query.d.ts +1 -1
- package/dist/src/codegen/extract/query.d.ts.map +1 -1
- package/dist/src/codegen/extract/states.d.ts +1 -1
- package/dist/src/codegen/extract/states.d.ts.map +1 -1
- package/dist/src/codegen/extract/type-helpers.d.ts +13 -0
- package/dist/src/codegen/extract/type-helpers.d.ts.map +1 -0
- package/dist/src/codegen/extract/type-helpers.js +98 -0
- package/dist/src/codegen/extract/type-helpers.js.map +1 -0
- package/dist/src/codegen/scaffoldFromSchema.d.ts +3 -3
- package/dist/src/codegen/scaffoldFromSchema.d.ts.map +1 -1
- package/dist/src/codegen/scaffoldFromSchema.js +202 -19
- package/dist/src/codegen/scaffoldFromSchema.js.map +1 -1
- package/dist/src/codegen/templates/command/commands.specs.ts +3 -3
- package/dist/src/codegen/templates/command/commands.ts.ejs +14 -9
- package/dist/src/codegen/templates/command/decide.specs.specs.ts +54 -54
- package/dist/src/codegen/templates/command/decide.specs.ts +13 -9
- package/dist/src/codegen/templates/command/decide.ts.ejs +1 -0
- package/dist/src/codegen/templates/command/events.specs.ts +3 -3
- package/dist/src/codegen/templates/command/events.ts.ejs +16 -13
- package/dist/src/codegen/templates/command/evolve.specs.ts +3 -3
- package/dist/src/codegen/templates/command/handle.specs.ts +10 -5
- package/dist/src/codegen/templates/command/mutation.resolver.specs.ts +8 -7
- package/dist/src/codegen/templates/command/mutation.resolver.ts.ejs +2 -23
- package/dist/src/codegen/templates/command/register.specs.ts +4 -4
- package/dist/src/codegen/templates/command/register.ts.ejs +1 -4
- package/dist/src/codegen/templates/command/state.specs.ts +54 -50
- package/dist/src/codegen/templates/command/state.ts.ejs +8 -4
- package/dist/src/codegen/templates/query/projection.specs.specs.ts +7 -7
- package/dist/src/codegen/templates/query/projection.specs.ts +24 -7
- package/dist/src/codegen/templates/query/projection.ts.ejs +64 -12
- package/dist/src/codegen/templates/query/query.resolver.specs.ts +19 -16
- package/dist/src/codegen/templates/query/query.resolver.ts.ejs +11 -49
- package/dist/src/codegen/templates/query/state.specs.ts +3 -3
- package/dist/src/codegen/templates/react/react.specs.specs.ts +3 -3
- package/dist/src/codegen/templates/react/react.specs.ts +3 -3
- package/dist/src/codegen/templates/react/react.ts.ejs +0 -1
- package/dist/src/codegen/templates/react/register.specs.ts +3 -3
- package/dist/src/codegen/templates/react/register.ts.ejs +0 -1
- package/dist/src/codegen/test-data/specVariant1.d.ts +1 -1
- package/dist/src/codegen/test-data/specVariant1.d.ts.map +1 -1
- package/dist/src/codegen/test-data/specVariant1.js +1 -1
- package/dist/src/codegen/test-data/specVariant1.js.map +1 -1
- package/dist/src/codegen/types.d.ts +1 -1
- package/dist/src/codegen/types.d.ts.map +1 -1
- package/dist/src/commands/generate-server.d.ts +0 -1
- package/dist/src/commands/generate-server.d.ts.map +1 -1
- package/dist/src/commands/generate-server.js +53 -31
- package/dist/src/commands/generate-server.js.map +1 -1
- package/dist/src/domain/shared/graphql-types.d.ts +10 -0
- package/dist/src/domain/shared/graphql-types.d.ts.map +1 -0
- package/dist/src/domain/shared/graphql-types.js +40 -0
- package/dist/src/domain/shared/graphql-types.js.map +1 -0
- package/dist/src/domain/shared/graphql-types.ts +20 -0
- package/dist/src/domain/shared/index.d.ts +1 -0
- package/dist/src/domain/shared/index.d.ts.map +1 -1
- package/dist/src/domain/shared/index.js +1 -0
- package/dist/src/domain/shared/index.js.map +1 -1
- package/dist/src/domain/shared/index.ts +1 -0
- package/dist/src/domain/shared/sendCommand.d.ts +1 -1
- package/dist/src/domain/shared/sendCommand.d.ts.map +1 -1
- package/dist/src/domain/shared/sendCommand.ts +1 -1
- package/dist/src/domain/shared/types.d.ts +5 -7
- package/dist/src/domain/shared/types.d.ts.map +1 -1
- package/dist/src/domain/shared/types.js +11 -38
- package/dist/src/domain/shared/types.js.map +1 -1
- package/dist/src/domain/shared/types.ts +10 -16
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/codegen/extract/commands.ts +1 -1
- package/src/codegen/extract/data-sink.ts +1 -1
- package/src/codegen/extract/events.ts +1 -1
- package/src/codegen/extract/gwt.ts +1 -1
- package/src/codegen/extract/index.ts +1 -0
- package/src/codegen/extract/messages.ts +1 -1
- package/src/codegen/extract/projection.ts +1 -1
- package/src/codegen/extract/query.ts +1 -1
- package/src/codegen/extract/states.ts +1 -1
- package/src/codegen/extract/type-helpers.ts +102 -0
- package/src/codegen/scaffoldFromSchema.ts +283 -25
- package/src/codegen/templates/command/commands.specs.ts +3 -3
- package/src/codegen/templates/command/commands.ts.ejs +14 -9
- package/src/codegen/templates/command/decide.specs.specs.ts +54 -54
- package/src/codegen/templates/command/decide.specs.ts +13 -9
- package/src/codegen/templates/command/decide.ts.ejs +1 -0
- package/src/codegen/templates/command/events.specs.ts +3 -3
- package/src/codegen/templates/command/events.ts.ejs +16 -13
- package/src/codegen/templates/command/evolve.specs.ts +3 -3
- package/src/codegen/templates/command/handle.specs.ts +10 -5
- package/src/codegen/templates/command/mutation.resolver.specs.ts +8 -7
- package/src/codegen/templates/command/mutation.resolver.ts.ejs +2 -23
- package/src/codegen/templates/command/register.specs.ts +4 -4
- package/src/codegen/templates/command/register.ts.ejs +1 -4
- package/src/codegen/templates/command/state.specs.ts +54 -50
- package/src/codegen/templates/command/state.ts.ejs +8 -4
- package/src/codegen/templates/query/projection.specs.specs.ts +7 -7
- package/src/codegen/templates/query/projection.specs.ts +24 -7
- package/src/codegen/templates/query/projection.ts.ejs +64 -12
- package/src/codegen/templates/query/query.resolver.specs.ts +19 -16
- package/src/codegen/templates/query/query.resolver.ts.ejs +11 -49
- package/src/codegen/templates/query/state.specs.ts +3 -3
- package/src/codegen/templates/react/react.specs.specs.ts +3 -3
- package/src/codegen/templates/react/react.specs.ts +3 -3
- package/src/codegen/templates/react/react.ts.ejs +0 -1
- package/src/codegen/templates/react/register.specs.ts +3 -3
- package/src/codegen/templates/react/register.ts.ejs +0 -1
- package/src/codegen/test-data/specVariant1.ts +2 -2
- package/src/codegen/types.ts +1 -1
- package/src/commands/generate-server.ts +63 -34
- package/src/domain/shared/graphql-types.ts +20 -0
- package/src/domain/shared/index.ts +1 -0
- package/src/domain/shared/sendCommand.ts +1 -1
- 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/
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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/
|
|
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
|
-
|
|
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.
|
|
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
|
-
<%
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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/
|
|
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
|
-
|
|
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.
|
|
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/
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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(
|
|
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/
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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/
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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/
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
126
|
-
|
|
129
|
+
// TODO: Replace with a discriminated union of domain states for the current slice
|
|
130
|
+
export type State = {};
|
|
127
131
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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: '
|
|
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: '
|
|
25
|
+
* status: 'pending';
|
|
25
26
|
* };
|
|
26
27
|
*
|
|
27
28
|
* export type InProgressTask = {
|
|
28
|
-
* status: '
|
|
29
|
+
* status: 'in_progress';
|
|
29
30
|
* startedAt: string;
|
|
30
31
|
* };
|
|
31
32
|
*
|
|
32
33
|
* export type CompletedTask = {
|
|
33
|
-
* status: '
|
|
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/
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
572
|
+
spec.narratives,
|
|
573
573
|
[
|
|
574
574
|
{
|
|
575
575
|
type: 'command',
|