@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,11 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
<%
|
|
2
|
+
const enumList = collectEnumNames(commands.flatMap(c => c.fields));
|
|
3
|
+
%>import { Command } from "@event-driven-io/emmett";
|
|
4
|
+
<% if (enumList.length > 0) { -%>
|
|
5
|
+
import { <%= enumList.join(', ') %> } from '../../../shared';
|
|
6
|
+
<% } -%>
|
|
2
7
|
<% for (const command of commands) { -%>
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
export type <%= pascalCase(command.type) %> = Command<
|
|
9
|
+
'<%= command.type %>',
|
|
10
|
+
{
|
|
11
|
+
<% for (const field of command.fields) { -%>
|
|
12
|
+
<%- field.name %>: <%- toTsFieldType(field.tsType) %>;
|
|
13
|
+
<% } -%>
|
|
14
|
+
}
|
|
15
|
+
>;
|
|
11
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('spec.ts.ejs', () => {
|
|
6
6
|
it('should generate a valid spec 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: [
|
|
@@ -91,66 +91,66 @@ describe('spec.ts.ejs', () => {
|
|
|
91
91
|
],
|
|
92
92
|
};
|
|
93
93
|
|
|
94
|
-
const plans = await generateScaffoldFilePlans(spec.
|
|
94
|
+
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
95
95
|
const specFile = plans.find((p) => p.outputPath.endsWith('specs.ts'));
|
|
96
96
|
|
|
97
97
|
expect(specFile?.contents).toMatchInlineSnapshot(`
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
"import { describe, it } from 'vitest';
|
|
99
|
+
import { DeciderSpecification } from '@event-driven-io/emmett';
|
|
100
|
+
import { decide } from './decide';
|
|
101
|
+
import { evolve } from './evolve';
|
|
102
|
+
import { initialState, State } from './state';
|
|
103
|
+
import type { ListingCreated } from './events';
|
|
104
|
+
import type { CreateListing } from './commands';
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
describe('Should create listing successfully', () => {
|
|
107
|
+
type Events = ListingCreated;
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
109
|
+
const given = DeciderSpecification.for<CreateListing, Events, State>({
|
|
110
|
+
decide,
|
|
111
|
+
evolve,
|
|
112
|
+
initialState,
|
|
113
|
+
});
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
115
|
+
it('User creates listing with valid data', () => {
|
|
116
|
+
given([])
|
|
117
|
+
.when({
|
|
118
|
+
type: 'CreateListing',
|
|
119
|
+
data: {
|
|
120
|
+
propertyId: 'listing_123',
|
|
121
|
+
title: 'blah',
|
|
122
|
+
pricePerNight: 250,
|
|
123
|
+
maxGuests: 4,
|
|
124
|
+
amenities: ['wifi', 'kitchen'],
|
|
125
|
+
available: true,
|
|
126
|
+
tags: ['some tag'],
|
|
127
|
+
rating: 4.8,
|
|
128
|
+
metadata: { foo: 'bar' },
|
|
129
|
+
listedAt: new Date('2024-01-15T10:00:00Z'),
|
|
130
|
+
},
|
|
131
|
+
metadata: { now: new Date() },
|
|
132
|
+
})
|
|
133
133
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
134
|
+
.then([
|
|
135
|
+
{
|
|
136
|
+
type: 'ListingCreated',
|
|
137
|
+
data: {
|
|
138
|
+
propertyId: 'listing_123',
|
|
139
|
+
listedAt: new Date('2024-01-15T10:00:00Z'),
|
|
140
|
+
rating: 4.8,
|
|
141
|
+
metadata: { foo: 'bar' },
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
]);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
"
|
|
148
|
+
`);
|
|
149
149
|
});
|
|
150
150
|
it('should include given events in the spec file when provided', async () => {
|
|
151
151
|
const spec: SpecsSchema = {
|
|
152
152
|
variant: 'specs',
|
|
153
|
-
|
|
153
|
+
narratives: [
|
|
154
154
|
{
|
|
155
155
|
name: 'Guest removes a listing',
|
|
156
156
|
slices: [
|
|
@@ -233,7 +233,7 @@ describe('spec.ts.ejs', () => {
|
|
|
233
233
|
],
|
|
234
234
|
};
|
|
235
235
|
|
|
236
|
-
const plans = await generateScaffoldFilePlans(spec.
|
|
236
|
+
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
237
237
|
const specFile = plans.find((p) => p.outputPath.endsWith('specs.ts'));
|
|
238
238
|
|
|
239
239
|
expect(specFile?.contents).toMatchInlineSnapshot(`
|
|
@@ -292,7 +292,7 @@ describe('spec.ts.ejs', () => {
|
|
|
292
292
|
it('should generate separate tests for multiple examples with different scenarios', async () => {
|
|
293
293
|
const spec: SpecsSchema = {
|
|
294
294
|
variant: 'specs',
|
|
295
|
-
|
|
295
|
+
narratives: [
|
|
296
296
|
{
|
|
297
297
|
name: 'Questionnaires',
|
|
298
298
|
slices: [
|
|
@@ -421,7 +421,7 @@ describe('spec.ts.ejs', () => {
|
|
|
421
421
|
],
|
|
422
422
|
};
|
|
423
423
|
|
|
424
|
-
const plans = await generateScaffoldFilePlans(spec.
|
|
424
|
+
const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
|
|
425
425
|
const specFile = plans.find((p) => p.outputPath.endsWith('specs.ts'));
|
|
426
426
|
|
|
427
427
|
expect(specFile?.contents).toMatchInlineSnapshot(`
|
|
@@ -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
|
}
|