@auto-engineer/server-generator-apollo-emmett 0.1.1

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 (272) hide show
  1. package/.tmp/server-test-output/server/package.json +26 -0
  2. package/.tmp/server-test-output/server/scripts/generate-schema.ts +31 -0
  3. package/.tmp/server-test-output/server/src/domain/flows/add-item/create-item/commands.ts +8 -0
  4. package/.tmp/server-test-output/server/src/domain/flows/add-item/create-item/decide.specs.ts +36 -0
  5. package/.tmp/server-test-output/server/src/domain/flows/add-item/create-item/decide.ts +33 -0
  6. package/.tmp/server-test-output/server/src/domain/flows/add-item/create-item/events.ts +10 -0
  7. package/.tmp/server-test-output/server/src/domain/flows/add-item/create-item/evolve.ts +28 -0
  8. package/.tmp/server-test-output/server/src/domain/flows/add-item/create-item/handle.ts +24 -0
  9. package/.tmp/server-test-output/server/src/domain/flows/add-item/create-item/mutation.resolver.ts +25 -0
  10. package/.tmp/server-test-output/server/src/domain/flows/add-item/create-item/register.ts +7 -0
  11. package/.tmp/server-test-output/server/src/domain/flows/add-item/create-item/state.ts +47 -0
  12. package/.tmp/server-test-output/server/src/domain/flows/add-item/get-available-items/projection.specs.ts +46 -0
  13. package/.tmp/server-test-output/server/src/domain/flows/add-item/get-available-items/projection.ts +38 -0
  14. package/.tmp/server-test-output/server/src/domain/flows/add-item/get-available-items/query.resolver.ts +39 -0
  15. package/.tmp/server-test-output/server/src/domain/flows/add-item/get-available-items/state.ts +5 -0
  16. package/.tmp/server-test-output/server/src/domain/flows/add-item/notify-new-item/commands.ts +8 -0
  17. package/.tmp/server-test-output/server/src/domain/flows/add-item/notify-new-item/decide.specs.ts +36 -0
  18. package/.tmp/server-test-output/server/src/domain/flows/add-item/notify-new-item/decide.ts +33 -0
  19. package/.tmp/server-test-output/server/src/domain/flows/add-item/notify-new-item/events.ts +3 -0
  20. package/.tmp/server-test-output/server/src/domain/flows/add-item/notify-new-item/evolve.ts +28 -0
  21. package/.tmp/server-test-output/server/src/domain/flows/add-item/notify-new-item/handle.ts +24 -0
  22. package/.tmp/server-test-output/server/src/domain/flows/add-item/notify-new-item/mutation.resolver.ts +25 -0
  23. package/.tmp/server-test-output/server/src/domain/flows/add-item/notify-new-item/register.ts +7 -0
  24. package/.tmp/server-test-output/server/src/domain/flows/add-item/notify-new-item/state.ts +47 -0
  25. package/.tmp/server-test-output/server/src/domain/flows/add-item/notify-on-new-item/react.specs.ts +49 -0
  26. package/.tmp/server-test-output/server/src/domain/flows/add-item/notify-on-new-item/react.ts +43 -0
  27. package/.tmp/server-test-output/server/src/domain/flows/add-item/notify-on-new-item/register.ts +24 -0
  28. package/.tmp/server-test-output/server/src/domain/shared/ReadModel.ts +26 -0
  29. package/.tmp/server-test-output/server/src/domain/shared/index.ts +4 -0
  30. package/.tmp/server-test-output/server/src/domain/shared/reactorSpecification.ts +257 -0
  31. package/.tmp/server-test-output/server/src/domain/shared/sendCommand.ts +21 -0
  32. package/.tmp/server-test-output/server/src/domain/shared/types.ts +31 -0
  33. package/.tmp/server-test-output/server/src/server.ts +43 -0
  34. package/.tmp/server-test-output/server/src/utils/index.ts +3 -0
  35. package/.tmp/server-test-output/server/src/utils/loadProjections.ts +30 -0
  36. package/.tmp/server-test-output/server/src/utils/loadRegisterFiles.ts +41 -0
  37. package/.tmp/server-test-output/server/src/utils/loadResolvers.ts +36 -0
  38. package/.tmp/server-test-output/server/tsconfig.json +19 -0
  39. package/.tmp/server-test-output/server/vitest.config.ts +7 -0
  40. package/.turbo/turbo-build.log +5 -0
  41. package/.turbo/turbo-format.log +57 -0
  42. package/.turbo/turbo-lint.log +5 -0
  43. package/.turbo/turbo-test.log +113 -0
  44. package/.turbo/turbo-type-check.log +4 -0
  45. package/CHANGELOG.md +141 -0
  46. package/DEBUG.md +177 -0
  47. package/LICENSE +10 -0
  48. package/README.md +185 -0
  49. package/dist/cli-manifest.d.ts +3 -0
  50. package/dist/cli-manifest.d.ts.map +1 -0
  51. package/dist/cli-manifest.js +8 -0
  52. package/dist/cli-manifest.js.map +1 -0
  53. package/dist/codegen/extract/commands.d.ts +19 -0
  54. package/dist/codegen/extract/commands.d.ts.map +1 -0
  55. package/dist/codegen/extract/commands.js +52 -0
  56. package/dist/codegen/extract/commands.js.map +1 -0
  57. package/dist/codegen/extract/data-sink.d.ts +6 -0
  58. package/dist/codegen/extract/data-sink.d.ts.map +1 -0
  59. package/dist/codegen/extract/data-sink.js +39 -0
  60. package/dist/codegen/extract/data-sink.js.map +1 -0
  61. package/dist/codegen/extract/events.d.ts +10 -0
  62. package/dist/codegen/extract/events.d.ts.map +1 -0
  63. package/dist/codegen/extract/events.js +32 -0
  64. package/dist/codegen/extract/events.js.map +1 -0
  65. package/dist/codegen/extract/fields.d.ts +3 -0
  66. package/dist/codegen/extract/fields.d.ts.map +1 -0
  67. package/dist/codegen/extract/fields.js +9 -0
  68. package/dist/codegen/extract/fields.js.map +1 -0
  69. package/dist/codegen/extract/graphql.d.ts +14 -0
  70. package/dist/codegen/extract/graphql.d.ts.map +1 -0
  71. package/dist/codegen/extract/graphql.js +81 -0
  72. package/dist/codegen/extract/graphql.js.map +1 -0
  73. package/dist/codegen/extract/gwt.d.ts +6 -0
  74. package/dist/codegen/extract/gwt.d.ts.map +1 -0
  75. package/dist/codegen/extract/gwt.js +61 -0
  76. package/dist/codegen/extract/gwt.js.map +1 -0
  77. package/dist/codegen/extract/index.d.ts +8 -0
  78. package/dist/codegen/extract/index.d.ts.map +1 -0
  79. package/dist/codegen/extract/index.js +8 -0
  80. package/dist/codegen/extract/index.js.map +1 -0
  81. package/dist/codegen/extract/messages.d.ts +24 -0
  82. package/dist/codegen/extract/messages.d.ts.map +1 -0
  83. package/dist/codegen/extract/messages.js +131 -0
  84. package/dist/codegen/extract/messages.js.map +1 -0
  85. package/dist/codegen/extract/projection.d.ts +4 -0
  86. package/dist/codegen/extract/projection.d.ts.map +1 -0
  87. package/dist/codegen/extract/projection.js +30 -0
  88. package/dist/codegen/extract/projection.js.map +1 -0
  89. package/dist/codegen/extract/query.d.ts +11 -0
  90. package/dist/codegen/extract/query.d.ts.map +1 -0
  91. package/dist/codegen/extract/query.js +11 -0
  92. package/dist/codegen/extract/query.js.map +1 -0
  93. package/dist/codegen/extract/states.d.ts +5 -0
  94. package/dist/codegen/extract/states.d.ts.map +1 -0
  95. package/dist/codegen/extract/states.js +35 -0
  96. package/dist/codegen/extract/states.js.map +1 -0
  97. package/dist/codegen/scaffoldFromSchema.d.ts +9 -0
  98. package/dist/codegen/scaffoldFromSchema.d.ts.map +1 -0
  99. package/dist/codegen/scaffoldFromSchema.integration.specs.d.ts +2 -0
  100. package/dist/codegen/scaffoldFromSchema.integration.specs.d.ts.map +1 -0
  101. package/dist/codegen/scaffoldFromSchema.integration.specs.js +59 -0
  102. package/dist/codegen/scaffoldFromSchema.integration.specs.js.map +1 -0
  103. package/dist/codegen/scaffoldFromSchema.js +326 -0
  104. package/dist/codegen/scaffoldFromSchema.js.map +1 -0
  105. package/dist/codegen/templates/command/commands.specs.ts +90 -0
  106. package/dist/codegen/templates/command/commands.ts.ejs +11 -0
  107. package/dist/codegen/templates/command/decide.specs.specs.ts +265 -0
  108. package/dist/codegen/templates/command/decide.specs.ts +542 -0
  109. package/dist/codegen/templates/command/decide.specs.ts.ejs +51 -0
  110. package/dist/codegen/templates/command/decide.ts.ejs +107 -0
  111. package/dist/codegen/templates/command/events.specs.ts +106 -0
  112. package/dist/codegen/templates/command/events.ts.ejs +14 -0
  113. package/dist/codegen/templates/command/evolve.specs.ts +102 -0
  114. package/dist/codegen/templates/command/evolve.ts.ejs +39 -0
  115. package/dist/codegen/templates/command/handle.specs.ts +313 -0
  116. package/dist/codegen/templates/command/handle.ts.ejs +111 -0
  117. package/dist/codegen/templates/command/mutation.resolver.specs.ts +115 -0
  118. package/dist/codegen/templates/command/mutation.resolver.ts.ejs +25 -0
  119. package/dist/codegen/templates/command/register.specs.ts +107 -0
  120. package/dist/codegen/templates/command/register.ts.ejs +12 -0
  121. package/dist/codegen/templates/command/state.specs.ts +127 -0
  122. package/dist/codegen/templates/command/state.ts.ejs +47 -0
  123. package/dist/codegen/templates/query/projection.specs.specs..ts +276 -0
  124. package/dist/codegen/templates/query/projection.specs.ts +348 -0
  125. package/dist/codegen/templates/query/projection.specs.ts.ejs +71 -0
  126. package/dist/codegen/templates/query/projection.ts.ejs +121 -0
  127. package/dist/codegen/templates/query/query.resolver.specs.ts +254 -0
  128. package/dist/codegen/templates/query/query.resolver.ts.ejs +98 -0
  129. package/dist/codegen/templates/query/state.specs.ts +70 -0
  130. package/dist/codegen/templates/query/state.ts.ejs +7 -0
  131. package/dist/codegen/templates/react/react.specs.specs.ts +214 -0
  132. package/dist/codegen/templates/react/react.specs.ts +241 -0
  133. package/dist/codegen/templates/react/react.specs.ts.ejs +80 -0
  134. package/dist/codegen/templates/react/react.ts.ejs +56 -0
  135. package/dist/codegen/templates/react/register.specs.ts +222 -0
  136. package/dist/codegen/templates/react/register.ts.ejs +39 -0
  137. package/dist/codegen/test-data/specVariant1.d.ts +4 -0
  138. package/dist/codegen/test-data/specVariant1.d.ts.map +1 -0
  139. package/dist/codegen/test-data/specVariant1.js +185 -0
  140. package/dist/codegen/test-data/specVariant1.js.map +1 -0
  141. package/dist/codegen/types.d.ts +35 -0
  142. package/dist/codegen/types.d.ts.map +1 -0
  143. package/dist/codegen/types.js +2 -0
  144. package/dist/codegen/types.js.map +1 -0
  145. package/dist/codegen/utils/path.d.ts +4 -0
  146. package/dist/codegen/utils/path.d.ts.map +1 -0
  147. package/dist/codegen/utils/path.js +18 -0
  148. package/dist/codegen/utils/path.js.map +1 -0
  149. package/dist/commands/generate-server.d.ts +81 -0
  150. package/dist/commands/generate-server.d.ts.map +1 -0
  151. package/dist/commands/generate-server.js +383 -0
  152. package/dist/commands/generate-server.js.map +1 -0
  153. package/dist/domain/shared/ReadModel.d.ts +10 -0
  154. package/dist/domain/shared/ReadModel.d.ts.map +1 -0
  155. package/dist/domain/shared/ReadModel.js +19 -0
  156. package/dist/domain/shared/ReadModel.js.map +1 -0
  157. package/dist/domain/shared/ReadModel.ts +26 -0
  158. package/dist/domain/shared/index.d.ts +5 -0
  159. package/dist/domain/shared/index.d.ts.map +1 -0
  160. package/dist/domain/shared/index.js +5 -0
  161. package/dist/domain/shared/index.js.map +1 -0
  162. package/dist/domain/shared/index.ts +4 -0
  163. package/dist/domain/shared/reactorSpecification.d.ts +35 -0
  164. package/dist/domain/shared/reactorSpecification.d.ts.map +1 -0
  165. package/dist/domain/shared/reactorSpecification.js +155 -0
  166. package/dist/domain/shared/reactorSpecification.js.map +1 -0
  167. package/dist/domain/shared/reactorSpecification.ts +257 -0
  168. package/dist/domain/shared/sendCommand.d.ts +4 -0
  169. package/dist/domain/shared/sendCommand.d.ts.map +1 -0
  170. package/dist/domain/shared/sendCommand.js +17 -0
  171. package/dist/domain/shared/sendCommand.js.map +1 -0
  172. package/dist/domain/shared/sendCommand.ts +21 -0
  173. package/dist/domain/shared/types.d.ts +19 -0
  174. package/dist/domain/shared/types.d.ts.map +1 -0
  175. package/dist/domain/shared/types.js +39 -0
  176. package/dist/domain/shared/types.js.map +1 -0
  177. package/dist/domain/shared/types.ts +31 -0
  178. package/dist/index.d.ts +3 -0
  179. package/dist/index.d.ts.map +1 -0
  180. package/dist/index.js +3 -0
  181. package/dist/index.js.map +1 -0
  182. package/dist/server.d.ts +2 -0
  183. package/dist/server.d.ts.map +1 -0
  184. package/dist/server.js +33 -0
  185. package/dist/server.js.map +1 -0
  186. package/dist/server.ts +43 -0
  187. package/dist/utils/index.d.ts +4 -0
  188. package/dist/utils/index.d.ts.map +1 -0
  189. package/dist/utils/index.js +4 -0
  190. package/dist/utils/index.js.map +1 -0
  191. package/dist/utils/index.ts +3 -0
  192. package/dist/utils/loadProjections.d.ts +3 -0
  193. package/dist/utils/loadProjections.d.ts.map +1 -0
  194. package/dist/utils/loadProjections.js +23 -0
  195. package/dist/utils/loadProjections.js.map +1 -0
  196. package/dist/utils/loadProjections.ts +30 -0
  197. package/dist/utils/loadRegisterFiles.d.ts +6 -0
  198. package/dist/utils/loadRegisterFiles.d.ts.map +1 -0
  199. package/dist/utils/loadRegisterFiles.js +28 -0
  200. package/dist/utils/loadRegisterFiles.js.map +1 -0
  201. package/dist/utils/loadRegisterFiles.ts +41 -0
  202. package/dist/utils/loadResolvers.d.ts +5 -0
  203. package/dist/utils/loadResolvers.d.ts.map +1 -0
  204. package/dist/utils/loadResolvers.js +27 -0
  205. package/dist/utils/loadResolvers.js.map +1 -0
  206. package/dist/utils/loadResolvers.ts +36 -0
  207. package/package.json +55 -0
  208. package/src/cli-manifest.ts +9 -0
  209. package/src/codegen/extract/commands.ts +79 -0
  210. package/src/codegen/extract/data-sink.ts +45 -0
  211. package/src/codegen/extract/events.ts +46 -0
  212. package/src/codegen/extract/fields.ts +17 -0
  213. package/src/codegen/extract/graphql.ts +103 -0
  214. package/src/codegen/extract/gwt.ts +75 -0
  215. package/src/codegen/extract/index.ts +7 -0
  216. package/src/codegen/extract/messages.ts +196 -0
  217. package/src/codegen/extract/projection.ts +47 -0
  218. package/src/codegen/extract/query.ts +18 -0
  219. package/src/codegen/extract/states.ts +45 -0
  220. package/src/codegen/scaffoldFromSchema.integration.specs.ts +71 -0
  221. package/src/codegen/scaffoldFromSchema.ts +440 -0
  222. package/src/codegen/templates/command/commands.specs.ts +90 -0
  223. package/src/codegen/templates/command/commands.ts.ejs +11 -0
  224. package/src/codegen/templates/command/decide.specs.specs.ts +265 -0
  225. package/src/codegen/templates/command/decide.specs.ts +542 -0
  226. package/src/codegen/templates/command/decide.specs.ts.ejs +51 -0
  227. package/src/codegen/templates/command/decide.ts.ejs +107 -0
  228. package/src/codegen/templates/command/events.specs.ts +106 -0
  229. package/src/codegen/templates/command/events.ts.ejs +14 -0
  230. package/src/codegen/templates/command/evolve.specs.ts +102 -0
  231. package/src/codegen/templates/command/evolve.ts.ejs +39 -0
  232. package/src/codegen/templates/command/handle.specs.ts +313 -0
  233. package/src/codegen/templates/command/handle.ts.ejs +111 -0
  234. package/src/codegen/templates/command/mutation.resolver.specs.ts +115 -0
  235. package/src/codegen/templates/command/mutation.resolver.ts.ejs +25 -0
  236. package/src/codegen/templates/command/register.specs.ts +107 -0
  237. package/src/codegen/templates/command/register.ts.ejs +12 -0
  238. package/src/codegen/templates/command/state.specs.ts +127 -0
  239. package/src/codegen/templates/command/state.ts.ejs +47 -0
  240. package/src/codegen/templates/query/projection.specs.specs..ts +276 -0
  241. package/src/codegen/templates/query/projection.specs.ts +348 -0
  242. package/src/codegen/templates/query/projection.specs.ts.ejs +71 -0
  243. package/src/codegen/templates/query/projection.ts.ejs +121 -0
  244. package/src/codegen/templates/query/query.resolver.specs.ts +254 -0
  245. package/src/codegen/templates/query/query.resolver.ts.ejs +98 -0
  246. package/src/codegen/templates/query/state.specs.ts +70 -0
  247. package/src/codegen/templates/query/state.ts.ejs +7 -0
  248. package/src/codegen/templates/react/react.specs.specs.ts +214 -0
  249. package/src/codegen/templates/react/react.specs.ts +241 -0
  250. package/src/codegen/templates/react/react.specs.ts.ejs +80 -0
  251. package/src/codegen/templates/react/react.ts.ejs +56 -0
  252. package/src/codegen/templates/react/register.specs.ts +222 -0
  253. package/src/codegen/templates/react/register.ts.ejs +39 -0
  254. package/src/codegen/test-data/specVariant1.json +212 -0
  255. package/src/codegen/test-data/specVariant1.ts +188 -0
  256. package/src/codegen/test-data/specVariant2.json +396 -0
  257. package/src/codegen/types.ts +35 -0
  258. package/src/codegen/utils/path.ts +20 -0
  259. package/src/commands/generate-server.ts +517 -0
  260. package/src/domain/shared/ReadModel.ts +26 -0
  261. package/src/domain/shared/index.ts +4 -0
  262. package/src/domain/shared/reactorSpecification.ts +257 -0
  263. package/src/domain/shared/sendCommand.ts +21 -0
  264. package/src/domain/shared/types.ts +31 -0
  265. package/src/index.ts +2 -0
  266. package/src/server.ts +43 -0
  267. package/src/utils/index.ts +3 -0
  268. package/src/utils/loadProjections.ts +30 -0
  269. package/src/utils/loadRegisterFiles.ts +41 -0
  270. package/src/utils/loadResolvers.ts +36 -0
  271. package/tsconfig.json +12 -0
  272. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,106 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { SpecsSchemaType as SpecsSchema } from '@auto-engineer/flow';
3
+ import { generateScaffoldFilePlans } from '../../scaffoldFromSchema';
4
+
5
+ describe('events.ts.ejs', () => {
6
+ it('should generate an event file', async () => {
7
+ const spec: SpecsSchema = {
8
+ variant: 'specs',
9
+ flows: [
10
+ {
11
+ name: 'Host creates a listing',
12
+ slices: [
13
+ {
14
+ type: 'command',
15
+ name: 'Create listing',
16
+ client: {
17
+ description: 'test',
18
+ specs: [],
19
+ },
20
+ server: {
21
+ description: 'test',
22
+ gwt: [
23
+ {
24
+ when: {
25
+ commandRef: 'CreateListing',
26
+ exampleData: {
27
+ propertyId: 'listing_123',
28
+ title: 'blah',
29
+ pricePerNight: 250,
30
+ maxGuests: 4,
31
+ amenities: ['wifi', 'kitchen'],
32
+ available: true,
33
+ tags: ['some tag'],
34
+ rating: 4.8,
35
+ metadata: { foo: 'bar' },
36
+ listedAt: '2024-01-15T10:00:00Z',
37
+ },
38
+ },
39
+ then: [
40
+ {
41
+ eventRef: 'ListingCreated',
42
+ exampleData: {
43
+ propertyId: 'listing_123',
44
+ listedAt: '2024-01-15T10:00:00Z',
45
+ rating: 4.8,
46
+ metadata: { foo: 'bar' },
47
+ },
48
+ },
49
+ ],
50
+ },
51
+ ],
52
+ },
53
+ },
54
+ ],
55
+ },
56
+ ],
57
+ messages: [
58
+ {
59
+ type: 'command',
60
+ name: 'CreateListing',
61
+ fields: [],
62
+ },
63
+ {
64
+ type: 'event',
65
+ name: 'ListingCreated',
66
+ source: 'internal',
67
+ fields: [
68
+ { name: 'propertyId', type: 'string', required: true },
69
+ { name: 'listedAt', type: 'Date', required: true },
70
+ { name: 'rating', type: 'number', required: true },
71
+ { name: 'metadata', type: 'object', required: true },
72
+ ],
73
+ },
74
+ {
75
+ type: 'event',
76
+ name: 'ListingCreated',
77
+ source: 'internal',
78
+ fields: [
79
+ { name: 'propertyId', type: 'string', required: true },
80
+ { name: 'listedAt', type: 'Date', required: true },
81
+ { name: 'rating', type: 'number', required: true },
82
+ { name: 'metadata', type: 'object', required: true },
83
+ ],
84
+ },
85
+ ],
86
+ };
87
+
88
+ const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, undefined, 'src/domain/flows');
89
+ const eventFile = plans.find((p) => p.outputPath.endsWith('events.ts'));
90
+
91
+ expect(eventFile?.contents).toMatchInlineSnapshot(`
92
+ "import type { Event } from '@event-driven-io/emmett';
93
+
94
+ export type ListingCreated = Event<
95
+ 'ListingCreated',
96
+ {
97
+ propertyId: string;
98
+ listedAt: Date;
99
+ rating: number;
100
+ metadata: object;
101
+ }
102
+ >;
103
+ "
104
+ `);
105
+ });
106
+ });
@@ -0,0 +1,14 @@
1
+ <% if (events.length) { -%>
2
+ import type { Event } from '@event-driven-io/emmett';
3
+
4
+ <% for (const event of events) { -%>
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
+ <% } -%>
14
+ <% } -%>
@@ -0,0 +1,102 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { SpecsSchemaType as SpecsSchema } from '@auto-engineer/flow';
3
+ import { generateScaffoldFilePlans } from '../../scaffoldFromSchema';
4
+
5
+ describe('evolve.ts.ejs', () => {
6
+ it('should generate a valid evolve file from event structure', async () => {
7
+ const spec: SpecsSchema = {
8
+ variant: 'specs',
9
+ flows: [
10
+ {
11
+ name: 'Host creates a listing',
12
+ slices: [
13
+ {
14
+ type: 'command',
15
+ name: 'Create listing',
16
+ client: {
17
+ description: 'test',
18
+ specs: [],
19
+ },
20
+ server: {
21
+ description: 'test',
22
+ gwt: [
23
+ {
24
+ when: {
25
+ commandRef: 'CreateListing',
26
+ exampleData: {
27
+ propertyId: 'listing_123',
28
+ title: 'Some Apartment',
29
+ listedAt: '2024-01-15T10:00:00Z',
30
+ rating: 4.8,
31
+ metadata: { foo: 'bar' },
32
+ },
33
+ },
34
+ then: [
35
+ {
36
+ eventRef: 'ListingCreated',
37
+ exampleData: {
38
+ propertyId: 'listing_123',
39
+ listedAt: '2024-01-15T10:00:00Z',
40
+ rating: 4.8,
41
+ metadata: { foo: 'bar' },
42
+ },
43
+ },
44
+ ],
45
+ },
46
+ ],
47
+ },
48
+ },
49
+ ],
50
+ },
51
+ ],
52
+ messages: [
53
+ {
54
+ type: 'command',
55
+ name: 'CreateListing',
56
+ fields: [],
57
+ },
58
+ {
59
+ type: 'event',
60
+ name: 'ListingCreated',
61
+ source: 'internal',
62
+ fields: [],
63
+ },
64
+ ],
65
+ };
66
+
67
+ const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, undefined, 'src/domain/flows');
68
+ const evolveFile = plans.find((p) => p.outputPath.endsWith('evolve.ts'));
69
+
70
+ expect(evolveFile?.contents).toMatchInlineSnapshot(`
71
+ "import type { State } from './state';
72
+ import type { ListingCreated } from './events';
73
+
74
+ /**
75
+ * ## IMPLEMENTATION INSTRUCTIONS ##
76
+ *
77
+ * This function defines how the domain state evolves in response to events.
78
+ *
79
+ * Guidelines:
80
+ * - Apply only the **minimal** necessary changes for future decisions in \`decide.ts\`.
81
+ * - Ignore any event fields not required for decision-making logic.
82
+ * - If the event doesn’t change decision-relevant state, return the existing \`state\`.
83
+ * - Prefer immutability: always return a **new state object**.
84
+ * - Avoid spreading all of \`event.data\` unless all fields are relevant.
85
+ */
86
+
87
+ export const evolve = (state: State, event: ListingCreated): State => {
88
+ switch (event.type) {
89
+ case 'ListingCreated': {
90
+ // TODO: Update state based on ListingCreated
91
+ return {
92
+ ...state,
93
+ };
94
+ }
95
+ default:
96
+ return state;
97
+ }
98
+ };
99
+ "
100
+ `);
101
+ });
102
+ });
@@ -0,0 +1,39 @@
1
+ <% if (events.length) { -%>
2
+ import type { State } from './state';
3
+ <% events.forEach(event => { -%>
4
+ import type { <%= pascalCase(event.type) %> } from './events';
5
+ <% }); -%>
6
+
7
+ /**
8
+ * ## IMPLEMENTATION INSTRUCTIONS ##
9
+ *
10
+ * This function defines how the domain state evolves in response to events.
11
+ *
12
+ * Guidelines:
13
+ * - Apply only the **minimal** necessary changes for future decisions in `decide.ts`.
14
+ * - Ignore any event fields not required for decision-making logic.
15
+ * - If the event doesn’t change decision-relevant state, return the existing `state`.
16
+ * - Prefer immutability: always return a **new state object**.
17
+ * - Avoid spreading all of `event.data` unless all fields are relevant.
18
+ */
19
+
20
+ export const evolve = (
21
+ state: State,
22
+ event: <%= events.map(e => pascalCase(e.type)).join(' | ') %>
23
+ ): State => {
24
+ switch (event.type) {
25
+ <% events.forEach(event => { -%>
26
+ case '<%= event.type %>': {
27
+ // TODO: Update state based on <%= event.type %>
28
+ return {
29
+ ...state
30
+ };
31
+ }
32
+ <% }); -%>
33
+ default:
34
+ return state;
35
+ }
36
+ };
37
+ <% } else { -%>
38
+ // No events defined yet. Evolve logic will be generated once events exist.
39
+ <% } -%>
@@ -0,0 +1,313 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { generateScaffoldFilePlans } from '../../scaffoldFromSchema';
3
+ import { SpecsSchemaType as SpecsSchema } from '@auto-engineer/flow';
4
+
5
+ describe('generateScaffoldFilePlans', () => {
6
+ it('should generate a valid handle file', async () => {
7
+ const spec: SpecsSchema = {
8
+ variant: 'specs',
9
+ flows: [
10
+ {
11
+ name: 'Host creates a listing',
12
+ slices: [
13
+ {
14
+ type: 'command',
15
+ name: 'Create listing',
16
+ stream: 'listing-${propertyId}',
17
+ client: {
18
+ description: 'test',
19
+ specs: [],
20
+ },
21
+ server: {
22
+ description: 'test',
23
+ gwt: [
24
+ {
25
+ when: {
26
+ commandRef: 'CreateListing',
27
+ exampleData: {
28
+ propertyId: 'listing_123',
29
+ title: 'Modern Downtown Apartment',
30
+ listedAt: '2024-01-15T10:00:00Z',
31
+ rating: 4.8,
32
+ metadata: { foo: 'bar' },
33
+ },
34
+ },
35
+ then: [
36
+ {
37
+ eventRef: 'ListingCreated',
38
+ exampleData: {
39
+ propertyId: 'listing_123',
40
+ listedAt: '2024-01-15T10:00:00Z',
41
+ rating: 4.8,
42
+ metadata: { foo: 'bar' },
43
+ },
44
+ },
45
+ ],
46
+ },
47
+ ],
48
+ data: [
49
+ {
50
+ target: {
51
+ type: 'Event',
52
+ name: 'ListingCreated',
53
+ },
54
+ destination: {
55
+ type: 'stream',
56
+ pattern: 'listings-${propertyId}',
57
+ },
58
+ },
59
+ ],
60
+ },
61
+ },
62
+ ],
63
+ },
64
+ ],
65
+ messages: [
66
+ {
67
+ type: 'command',
68
+ name: 'CreateListing',
69
+ fields: [
70
+ { name: 'propertyId', type: 'string', required: true },
71
+ { name: 'title', type: 'string', required: true },
72
+ { name: 'listedAt', type: 'Date', required: true },
73
+ { name: 'rating', type: 'number', required: true },
74
+ { name: 'metadata', type: 'object', required: true },
75
+ ],
76
+ },
77
+ {
78
+ type: 'event',
79
+ name: 'ListingCreated',
80
+ source: 'internal',
81
+ fields: [
82
+ { name: 'propertyId', type: 'string', required: true },
83
+ { name: 'title', type: 'string', required: true },
84
+ { name: 'listedAt', type: 'Date', required: true },
85
+ { name: 'rating', type: 'number', required: true },
86
+ { name: 'metadata', type: 'object', required: true },
87
+ ],
88
+ },
89
+ ],
90
+ };
91
+
92
+ const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, undefined, 'src/domain/flows');
93
+ const handleFile = plans.find((p) => p.outputPath.endsWith('handle.ts'));
94
+
95
+ expect(handleFile?.contents).toMatchInlineSnapshot(`
96
+ "import { CommandHandler, type EventStore, type MessageHandlerResult } from '@event-driven-io/emmett';
97
+ import { evolve } from './evolve';
98
+ import { initialState } from './state';
99
+ import { decide } from './decide';
100
+ import type { CreateListing } from './commands';
101
+
102
+ const handler = CommandHandler({
103
+ evolve,
104
+ initialState,
105
+ });
106
+
107
+ export const handle = async (eventStore: EventStore, command: CreateListing): Promise<MessageHandlerResult> => {
108
+ const streamId = \`listings-\${command.data.propertyId}\`;
109
+
110
+ try {
111
+ await handler(eventStore, streamId, (state) => decide(command, state));
112
+ return; // success (returns void)
113
+ } catch (error: any) {
114
+ return {
115
+ type: 'SKIP',
116
+ reason: \`Command failed: \${error?.message ?? 'Unknown'}\`,
117
+ };
118
+ }
119
+ };
120
+ "
121
+ `);
122
+ });
123
+ it('should generate a valid handle file with integration', async () => {
124
+ const spec: SpecsSchema = {
125
+ variant: 'specs',
126
+ flows: [
127
+ {
128
+ name: 'Assistant suggests items',
129
+ slices: [
130
+ {
131
+ type: 'command',
132
+ name: 'Suggest Items',
133
+ stream: 'session-${sessionId}',
134
+ client: {
135
+ description: 'test',
136
+ specs: [],
137
+ },
138
+ server: {
139
+ description: '',
140
+ data: [
141
+ {
142
+ target: {
143
+ type: 'Command',
144
+ name: 'SuggestItems',
145
+ },
146
+ destination: {
147
+ type: 'integration',
148
+ systems: ['AI'],
149
+ message: {
150
+ name: 'DoChat',
151
+ type: 'command',
152
+ },
153
+ },
154
+ _additionalInstructions: 'Ensure systemPrompt includes product catalogue guidance',
155
+ _withState: {
156
+ target: {
157
+ type: 'State',
158
+ name: 'Products',
159
+ },
160
+ origin: {
161
+ type: 'integration',
162
+ systems: ['product-catalog'],
163
+ },
164
+ },
165
+ },
166
+ {
167
+ target: {
168
+ type: 'Event',
169
+ name: 'ItemsSuggested',
170
+ },
171
+ destination: {
172
+ type: 'stream',
173
+ pattern: 'session-${sessionId}',
174
+ },
175
+ },
176
+ ],
177
+ gwt: [
178
+ {
179
+ when: {
180
+ commandRef: 'SuggestItems',
181
+ exampleData: {
182
+ sessionId: 'session-123',
183
+ prompt: 'What should I buy?',
184
+ },
185
+ },
186
+ then: [
187
+ {
188
+ eventRef: 'ItemsSuggested',
189
+ exampleData: {
190
+ sessionId: 'session-123',
191
+ items: [],
192
+ },
193
+ },
194
+ ],
195
+ },
196
+ ],
197
+ },
198
+ },
199
+ ],
200
+ },
201
+ ],
202
+ messages: [
203
+ {
204
+ type: 'command',
205
+ name: 'SuggestItems',
206
+ fields: [
207
+ { name: 'sessionId', type: 'string', required: true },
208
+ { name: 'prompt', type: 'string', required: true },
209
+ ],
210
+ },
211
+ {
212
+ type: 'command',
213
+ name: 'DoChat',
214
+ fields: [
215
+ { name: 'sessionId', type: 'string', required: true },
216
+ { name: 'prompt', type: 'string', required: true },
217
+ { name: 'systemPrompt', type: 'string', required: false },
218
+ ],
219
+ },
220
+ {
221
+ type: 'event',
222
+ name: 'ItemsSuggested',
223
+ source: 'internal',
224
+ fields: [
225
+ { name: 'sessionId', type: 'string', required: true },
226
+ { name: 'items', type: 'Array<object>', required: true },
227
+ ],
228
+ },
229
+ {
230
+ type: 'state',
231
+ name: 'Products',
232
+ fields: [
233
+ {
234
+ name: 'products',
235
+ type: 'Array<{ id: string, name: string }>',
236
+ required: true,
237
+ },
238
+ ],
239
+ },
240
+ ],
241
+ integrations: [
242
+ {
243
+ name: 'AI',
244
+ source: '@auto-engineer/ai-integration',
245
+ description: '',
246
+ },
247
+ {
248
+ name: 'product-catalog',
249
+ source: '@auto-engineer/product-catalogue-integration',
250
+ description: '',
251
+ },
252
+ ],
253
+ };
254
+
255
+ const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, spec.integrations, 'src/domain/flows');
256
+ const handleFile = plans.find((p) => p.outputPath.endsWith('handle.ts'));
257
+
258
+ expect(handleFile?.contents).toMatchInlineSnapshot(`
259
+ "import '@auto-engineer/product-catalogue-integration';
260
+
261
+ import { AI } from '@auto-engineer/ai-integration';
262
+
263
+ import { Products } from '@auto-engineer/product-catalogue-integration';
264
+
265
+ import { CommandHandler, type EventStore, type MessageHandlerResult } from '@event-driven-io/emmett';
266
+ import { evolve } from './evolve';
267
+ import { initialState } from './state';
268
+ import { decide } from './decide';
269
+ import type { SuggestItems } from './commands';
270
+
271
+ /**
272
+ * ## IMPLEMENTATION INSTRUCTIONS ##
273
+ * Ensure systemPrompt includes product catalogue guidance
274
+ */
275
+
276
+ const handler = CommandHandler({
277
+ evolve,
278
+ initialState,
279
+ });
280
+
281
+ export const handle = async (eventStore: EventStore, command: SuggestItems): Promise<MessageHandlerResult> => {
282
+ const streamId = \`session-\${command.data.sessionId}\`;
283
+
284
+ try {
285
+ // TODO: Map fields from the incoming command to this integration input.
286
+ // - Use relevant fields from \`command.data\` to populate the required inputs below.
287
+ // - Some fields may require transformation or enrichment.
288
+ // - If additional context is needed, construct it here.
289
+ // const products: Products | undefined = await AI.Commands?.DoChat<Products>({
290
+ // type: 'DoChat',
291
+ // data: {
292
+ // // sessionId: ???
293
+ // prompt: ???
294
+ // systemPrompt: ???
295
+ // },
296
+ // });
297
+
298
+ await handler(eventStore, streamId, (state) =>
299
+ // TODO: add products as a parameter to decide once implemented above
300
+ decide(command, state /* products */),
301
+ );
302
+ return; // success (returns void)
303
+ } catch (error: any) {
304
+ return {
305
+ type: 'SKIP',
306
+ reason: \`Command failed: \${error?.message ?? 'Unknown'}\`,
307
+ };
308
+ }
309
+ };
310
+ "
311
+ `);
312
+ });
313
+ });
@@ -0,0 +1,111 @@
1
+ <%
2
+ const integrationData = (slice.server?.data ?? []).filter(d => d.destination?.type === 'integration');
3
+
4
+ const integrationImports = new Map();
5
+ const integrationSideEffectImports = new Set();
6
+
7
+ for (const d of integrationData) {
8
+ for (const system of d.destination?.systems ?? []) {
9
+ const integration = (integrations ?? []).find(i => i.name === system);
10
+ if (integration) {
11
+ integrationImports.set(system, integration.source);
12
+ }
13
+ }
14
+
15
+ const returnType = d._withState?.target?.name;
16
+ const returnSystem = d._withState?.origin?.systems?.[0];
17
+ if (returnType && returnSystem) {
18
+ const integration = (integrations ?? []).find(i => i.name === returnSystem);
19
+ if (integration) {
20
+ integrationImports.set(returnType, integration.source);
21
+ integrationSideEffectImports.add(integration.source);
22
+ }
23
+ }
24
+ }
25
+
26
+ const additionalInstructions = (slice.server?.data ?? [])
27
+ .map(d => d._additionalInstructions)
28
+ .filter(Boolean);
29
+
30
+ const integrationCalls = integrationData.map((d, i) => {
31
+ const system = d.destination.systems?.[0];
32
+ const messageName = d.destination.message?.name;
33
+ const returnType = d._withState?.target?.name;
34
+ const varName = returnType ? camelCase(returnType) : `result${i + 1}`;
35
+
36
+ const inputFields = messages.find(m => m.name === messageName && m.type === 'command')?.fields ?? [];
37
+ const inputLines = inputFields.map(f => ` // ${f.name}: ???`);
38
+
39
+ return {
40
+ varName,
41
+ call: `// TODO: Map fields from the incoming command to this integration input.
42
+ // - Use relevant fields from \`command.data\` to populate the required inputs below.
43
+ // - Some fields may require transformation or enrichment.
44
+ // - If additional context is needed, construct it here.
45
+ // const ${varName}${returnType ? `: ${returnType} | undefined` : ''} = await ${system}.Commands?.${messageName}${returnType ? `<${returnType}>` : ''}({
46
+ // type: '${messageName}',
47
+ // data: {
48
+ //${inputLines.join('\n')}
49
+ // },
50
+ // });`,
51
+ };
52
+ });
53
+
54
+ const resultVarName = integrationCalls.find(call => !!call.varName)?.varName;
55
+ const needsReturnValue = typeof resultVarName === 'string';
56
+ %>
57
+
58
+ <% integrationSideEffectImports.forEach((importSource) => { %>
59
+ import '<%= importSource %>';
60
+ <% }); %>
61
+ <% integrationImports.forEach((importSource, importName) => { %>
62
+ import { <%= importName %> } from '<%= importSource %>';
63
+ <% }); %>
64
+ import { CommandHandler, type EventStore, type MessageHandlerResult } from '@event-driven-io/emmett';
65
+ import { evolve } from './evolve';
66
+ import { initialState } from './state';
67
+ import { decide } from './decide';
68
+ import type { <%= commands.map(c => pascalCase(c.type)).join(', ') %> } from './commands';
69
+
70
+ <% if (additionalInstructions.length) { -%>
71
+ /**
72
+ * ## IMPLEMENTATION INSTRUCTIONS ##
73
+ <% additionalInstructions.forEach(instr => { -%>
74
+ * <%= instr %>
75
+ <% }) -%>
76
+ */
77
+ <% } -%>
78
+
79
+ const handler = CommandHandler({
80
+ evolve,
81
+ initialState,
82
+ });
83
+
84
+ export const handle = async (
85
+ eventStore: EventStore,
86
+ command: <%= commands.map(c => pascalCase(c.type)).join(' | ') %>
87
+ ): Promise<MessageHandlerResult> => {
88
+ <% if (stream?.pattern?.includes('${')) { -%>
89
+ const streamId = `<%= stream.pattern.replace(/\$\{([^}]+)\}/g, (_, key) => `\${command.data.${key}}`) %>`;
90
+ <% } else { -%>
91
+ const streamId = '<%= stream?.pattern ?? 'unknown-stream' %>';
92
+ <% } -%>
93
+
94
+ try {
95
+ <% integrationCalls.forEach(({ call }) => { -%>
96
+ <%= call %>
97
+
98
+ <% }) -%>
99
+ await handler(eventStore, streamId, (state) =>
100
+ <%- needsReturnValue
101
+ ? ` // TODO: add ${resultVarName} as a parameter to decide once implemented above\n decide(command, state, /* ${resultVarName} */)`
102
+ : ` decide(command, state)` %>
103
+ );
104
+ return; // success (returns void)
105
+ } catch (error: any) {
106
+ return {
107
+ type: 'SKIP',
108
+ reason: `Command failed: ${error?.message ?? 'Unknown'}`,
109
+ };
110
+ }
111
+ };