@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,214 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { SpecsSchemaType as SpecsSchema } from '@auto-engineer/flow';
3
+ import { generateScaffoldFilePlans } from '../../scaffoldFromSchema';
4
+
5
+ describe('react.specs.ts.ejs (react slice)', () => {
6
+ it('should generate correct react.specs.ts', async () => {
7
+ const spec: SpecsSchema = {
8
+ variant: 'specs',
9
+ flows: [
10
+ {
11
+ name: 'manage bookings',
12
+ slices: [
13
+ {
14
+ type: 'command',
15
+ name: 'guest submits booking request',
16
+ client: { description: '', specs: [] },
17
+ server: {
18
+ description: '',
19
+ gwt: [
20
+ {
21
+ when: {
22
+ commandRef: 'RequestBooking',
23
+ exampleData: {
24
+ propertyId: 'listing_123',
25
+ hostId: 'host_123',
26
+ guestId: 'guest_456',
27
+ checkIn: '2025-07-15',
28
+ checkOut: '2025-07-18',
29
+ guests: 2,
30
+ message: 'Looking forward to my stay!',
31
+ metadata: { now: 'bar', bookingId: '123' },
32
+ },
33
+ },
34
+ then: [
35
+ {
36
+ eventRef: 'BookingRequested',
37
+ exampleData: {
38
+ bookingId: 'book_xyz789',
39
+ hostId: 'host_123',
40
+ propertyId: 'prop_789',
41
+ guestId: 'guest_456',
42
+ checkIn: '2025-07-15',
43
+ checkOut: '2025-07-18',
44
+ guests: 2,
45
+ message: 'Hey',
46
+ status: 'pending_host_approval',
47
+ requestedAt: '2025-06-10T16:30:00.000Z',
48
+ expiresAt: '2025-06-11T16:30:00.000Z',
49
+ },
50
+ },
51
+ ],
52
+ },
53
+ ],
54
+ },
55
+ },
56
+ {
57
+ type: 'react',
58
+ name: 'Send notification to host',
59
+ server: {
60
+ description: 'Sends a host notification command in response to BookingRequested',
61
+ data: [
62
+ {
63
+ target: { type: 'Command', name: 'NotifyHost' },
64
+ destination: { type: 'stream', pattern: 'booking-${hostId}' },
65
+ },
66
+ ],
67
+ gwt: [
68
+ {
69
+ when: [
70
+ {
71
+ eventRef: 'BookingRequested',
72
+ exampleData: {
73
+ bookingId: 'book_xyz789',
74
+ hostId: 'host_123',
75
+ propertyId: 'prop_789',
76
+ guestId: 'guest_456',
77
+ checkIn: '2025-07-15',
78
+ checkOut: '2025-07-18',
79
+ guests: 2,
80
+ message: 'Hey',
81
+ status: 'pending_host_approval',
82
+ requestedAt: '2025-06-10T16:30:00.000Z',
83
+ expiresAt: '2025-06-11T16:30:00.000Z',
84
+ },
85
+ },
86
+ ],
87
+ then: [
88
+ {
89
+ commandRef: 'NotifyHost',
90
+ exampleData: {
91
+ hostId: 'host_123',
92
+ notificationType: 'booking_request',
93
+ priority: 'high',
94
+ channels: ['email', 'push'],
95
+ message: 'A guest has requested to book your place.',
96
+ actionRequired: true,
97
+ },
98
+ },
99
+ ],
100
+ },
101
+ ],
102
+ },
103
+ },
104
+ ],
105
+ },
106
+ ],
107
+ messages: [
108
+ {
109
+ type: 'command',
110
+ name: 'RequestBooking',
111
+ fields: [
112
+ { name: 'propertyId', type: 'string', required: true },
113
+ { name: 'hostId', type: 'string', required: true },
114
+ { name: 'guestId', type: 'string', required: true },
115
+ { name: 'checkIn', type: 'date', required: true },
116
+ { name: 'checkOut', type: 'date', required: true },
117
+ { name: 'guests', type: 'number', required: true },
118
+ { name: 'message', type: 'string', required: false },
119
+ ],
120
+ },
121
+ {
122
+ type: 'command',
123
+ name: 'NotifyHost',
124
+ fields: [
125
+ { name: 'hostId', type: 'string', required: true },
126
+ { name: 'notificationType', type: 'string', required: true },
127
+ { name: 'priority', type: 'string', required: true },
128
+ { name: 'channels', type: 'string[]', required: true },
129
+ { name: 'message', type: 'string', required: true },
130
+ { name: 'actionRequired', type: 'boolean', required: true },
131
+ ],
132
+ },
133
+ {
134
+ type: 'event',
135
+ name: 'BookingRequested',
136
+ source: 'internal',
137
+ fields: [
138
+ { name: 'bookingId', type: 'string', required: true },
139
+ { name: 'hostId', type: 'string', required: true },
140
+ { name: 'message', type: 'string', required: true },
141
+ ],
142
+ },
143
+ ],
144
+ };
145
+
146
+ const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, undefined, 'src/domain/flows');
147
+
148
+ const specFile = plans.find((p) => p.outputPath.endsWith('react.specs.ts'));
149
+ expect(specFile?.contents).toMatchInlineSnapshot(`
150
+ "import { describe, it, beforeEach } from 'vitest';
151
+ import 'reflect-metadata';
152
+ import { getInMemoryEventStore, type InMemoryEventStore, type CommandSender } from '@event-driven-io/emmett';
153
+ import { type ReactorContext, ReactorSpecification } from '../../../shared';
154
+ import { react } from './react';
155
+ import type { BookingRequested } from '../guest-submits-booking-request/events';
156
+ import type { NotifyHost } from '../send-notification-to-host/commands';
157
+
158
+ describe('ManageBookings | SendNotificationToHost', () => {
159
+ let eventStore: InMemoryEventStore;
160
+ let given: ReactorSpecification<BookingRequested, NotifyHost, ReactorContext>;
161
+ let messageBus: CommandSender;
162
+
163
+ beforeEach(() => {
164
+ eventStore = getInMemoryEventStore({});
165
+ given = ReactorSpecification.for<BookingRequested, NotifyHost, ReactorContext>(
166
+ () => react({ eventStore, commandSender: messageBus }),
167
+ (commandSender) => {
168
+ messageBus = commandSender;
169
+ return {
170
+ eventStore,
171
+ commandSender,
172
+ database: eventStore.database,
173
+ };
174
+ },
175
+ );
176
+ });
177
+
178
+ it('should send NotifyHost when BookingRequested is received', async () => {
179
+ await given([])
180
+ .when({
181
+ type: 'BookingRequested',
182
+ data: {
183
+ bookingId: 'book_xyz789',
184
+ hostId: 'host_123',
185
+ propertyId: 'prop_789',
186
+ guestId: 'guest_456',
187
+ checkIn: '2025-07-15',
188
+ checkOut: '2025-07-18',
189
+ guests: 2,
190
+ message: 'Hey',
191
+ status: 'pending_host_approval',
192
+ requestedAt: '2025-06-10T16:30:00.000Z',
193
+ expiresAt: '2025-06-11T16:30:00.000Z',
194
+ },
195
+ })
196
+
197
+ .then({
198
+ type: 'NotifyHost',
199
+ kind: 'Command',
200
+ data: {
201
+ hostId: 'host_123',
202
+ notificationType: 'booking_request',
203
+ priority: 'high',
204
+ channels: ['email', 'push'],
205
+ message: 'A guest has requested to book your place.',
206
+ actionRequired: true,
207
+ },
208
+ });
209
+ });
210
+ });
211
+ "
212
+ `);
213
+ });
214
+ });
@@ -0,0 +1,241 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { SpecsSchemaType as SpecsSchema } from '@auto-engineer/flow';
3
+ import { generateScaffoldFilePlans } from '../../scaffoldFromSchema';
4
+
5
+ describe('handle.ts.ejs (react slice)', () => {
6
+ it('should generate correct react.ts', async () => {
7
+ const spec: SpecsSchema = {
8
+ variant: 'specs',
9
+ flows: [
10
+ {
11
+ name: 'manage bookings',
12
+ slices: [
13
+ {
14
+ type: 'command',
15
+ name: 'guest submits booking request',
16
+ client: { description: '', specs: [] },
17
+ server: {
18
+ description: '',
19
+ gwt: [
20
+ {
21
+ when: {
22
+ commandRef: 'RequestBooking',
23
+ exampleData: {
24
+ propertyId: 'listing_123',
25
+ hostId: 'host_123',
26
+ guestId: 'guest_456',
27
+ checkIn: '2025-07-15',
28
+ checkOut: '2025-07-18',
29
+ guests: 2,
30
+ message: 'Looking forward to my stay!',
31
+ metadata: { now: 'bar', bookingId: '123' },
32
+ },
33
+ },
34
+ then: [
35
+ {
36
+ eventRef: 'BookingRequested',
37
+ exampleData: {
38
+ bookingId: 'book_xyz789',
39
+ hostId: 'host_123',
40
+ propertyId: 'prop_789',
41
+ guestId: 'guest_456',
42
+ checkIn: '2025-07-15',
43
+ checkOut: '2025-07-18',
44
+ guests: 2,
45
+ message: 'Hey',
46
+ status: 'pending_host_approval',
47
+ requestedAt: '2025-06-10T16:30:00.000Z',
48
+ expiresAt: '2025-06-11T16:30:00.000Z',
49
+ },
50
+ },
51
+ ],
52
+ },
53
+ ],
54
+ },
55
+ },
56
+ {
57
+ type: 'react',
58
+ name: 'Send notification to host',
59
+ server: {
60
+ description: 'Sends a host notification command in response to BookingRequested',
61
+ gwt: [
62
+ {
63
+ when: [
64
+ {
65
+ eventRef: 'BookingRequested',
66
+ exampleData: {
67
+ bookingId: 'book_xyz789',
68
+ hostId: 'host_123',
69
+ propertyId: 'prop_789',
70
+ guestId: 'guest_456',
71
+ checkIn: '2025-07-15',
72
+ checkOut: '2025-07-18',
73
+ guests: 2,
74
+ message: 'Hey',
75
+ status: 'pending_host_approval',
76
+ requestedAt: '2025-06-10T16:30:00.000Z',
77
+ expiresAt: '2025-06-11T16:30:00.000Z',
78
+ },
79
+ },
80
+ ],
81
+ then: [
82
+ {
83
+ commandRef: 'NotifyHost',
84
+ exampleData: {
85
+ hostId: 'host_123',
86
+ notificationType: 'booking_request',
87
+ priority: 'high',
88
+ channels: ['email', 'push'],
89
+ message: 'A guest has requested to book your place.',
90
+ actionRequired: true,
91
+ },
92
+ },
93
+ ],
94
+ },
95
+ ],
96
+ },
97
+ },
98
+ {
99
+ type: 'command',
100
+ name: 'notify host',
101
+ client: { description: '', specs: [] },
102
+ server: {
103
+ description: '',
104
+ gwt: [
105
+ {
106
+ when: {
107
+ commandRef: 'NotifyHost',
108
+ exampleData: {
109
+ hostId: 'host_123',
110
+ notificationType: 'booking_request',
111
+ priority: 'high',
112
+ channels: ['email', 'push'],
113
+ message: 'A guest has requested to book your place.',
114
+ actionRequired: true,
115
+ },
116
+ },
117
+ then: [
118
+ {
119
+ eventRef: 'HostNotified',
120
+ exampleData: {
121
+ bookingId: 'book_xyz789',
122
+ hostId: 'host_123',
123
+ notificationType: 'booking_request',
124
+ channels: ['email', 'push'],
125
+ message: 'hi.',
126
+ notifiedAt: '2025-06-10T16:30:00.000Z',
127
+ actionRequired: true,
128
+ },
129
+ },
130
+ ],
131
+ },
132
+ ],
133
+ },
134
+ },
135
+ ],
136
+ },
137
+ ],
138
+ messages: [
139
+ {
140
+ type: 'command',
141
+ name: 'RequestBooking',
142
+ fields: [
143
+ { name: 'propertyId', type: 'string', required: true },
144
+ { name: 'hostId', type: 'string', required: true },
145
+ { name: 'guestId', type: 'string', required: true },
146
+ { name: 'checkIn', type: 'date', required: true },
147
+ { name: 'checkOut', type: 'date', required: true },
148
+ { name: 'guests', type: 'number', required: true },
149
+ { name: 'message', type: 'string', required: false },
150
+ ],
151
+ },
152
+ {
153
+ type: 'command',
154
+ name: 'NotifyHost',
155
+ fields: [
156
+ { name: 'hostId', type: 'string', required: true },
157
+ { name: 'notificationType', type: 'string', required: true },
158
+ { name: 'priority', type: 'string', required: true },
159
+ { name: 'channels', type: 'string[]', required: true },
160
+ { name: 'message', type: 'string', required: true },
161
+ { name: 'actionRequired', type: 'boolean', required: true },
162
+ ],
163
+ },
164
+ {
165
+ type: 'event',
166
+ name: 'BookingRequested',
167
+ source: 'internal',
168
+ fields: [
169
+ { name: 'bookingId', type: 'string', required: true },
170
+ { name: 'hostId', type: 'string', required: true },
171
+ { name: 'message', type: 'string', required: true },
172
+ ],
173
+ },
174
+ {
175
+ type: 'event',
176
+ name: 'HostNotified',
177
+ source: 'internal',
178
+ fields: [
179
+ { name: 'bookingId', type: 'string', required: true },
180
+ { name: 'hostId', type: 'string', required: true },
181
+ { name: 'notificationType', type: 'string', required: true },
182
+ { name: 'channels', type: 'string[]', required: true },
183
+ { name: 'notifiedAt', type: 'date', required: true },
184
+ { name: 'actionRequired', type: 'boolean', required: true },
185
+ { name: 'message', type: 'string', required: true },
186
+ ],
187
+ },
188
+ ],
189
+ };
190
+
191
+ const plans = await generateScaffoldFilePlans(spec.flows, spec.messages, undefined, 'src/domain/flows');
192
+ const handleFile = plans.find((p) => p.outputPath.endsWith('react.ts'));
193
+
194
+ expect(handleFile?.contents).toMatchInlineSnapshot(`
195
+ "import { inMemoryReactor, type MessageHandlerResult, IllegalStateError } from '@event-driven-io/emmett';
196
+ import type { BookingRequested } from '../guest-submits-booking-request/events';
197
+ import type { ReactorContext } from '../../../shared';
198
+
199
+ export const react = ({ eventStore, commandSender }: ReactorContext) =>
200
+ inMemoryReactor<BookingRequested>({
201
+ processorId: 'manage-bookings-send-notification-to-host',
202
+ canHandle: ['BookingRequested'],
203
+ connectionOptions: {
204
+ database: eventStore.database,
205
+ },
206
+ eachMessage: async (event, context): Promise<MessageHandlerResult> => {
207
+ /**
208
+ * ## IMPLEMENTATION INSTRUCTIONS ##
209
+ *
210
+ * - Inspect event data to determine if the command should be sent.
211
+ * - Replace the placeholder logic and \\\`throw\\\` below with real implementation.
212
+ * - Send one or more commands via: context.commandSender.send({...})
213
+ * - Optionally return a MessageHandlerResult for SKIP or error cases.
214
+ */
215
+
216
+ throw new IllegalStateError('Not yet implemented: react in response to BookingRequested');
217
+
218
+ // Example:
219
+ // if (event.data.status !== 'expected') {
220
+ // return {
221
+ // type: 'SKIP',
222
+ // reason: 'Condition not met',
223
+ // };
224
+ // }
225
+
226
+ // await context.commandSender.send({
227
+ // type: 'NotifyHost',
228
+ // kind: 'Command',
229
+ // data: {
230
+ // // Map event fields to command fields here
231
+ // // e.g., userId: event.data.userId,
232
+ // },
233
+ // });
234
+
235
+ // return;
236
+ },
237
+ });
238
+ "
239
+ `);
240
+ });
241
+ });
@@ -0,0 +1,80 @@
1
+ <%
2
+ const gwtList = slice.server?.gwt ?? [];
3
+ const firstGwt = gwtList[0];
4
+ const firstWhen = Array.isArray(firstGwt?.when) ? firstGwt.when[0] : firstGwt?.when;
5
+ const firstThen = Array.isArray(firstGwt?.then) ? firstGwt.then[0] : firstGwt?.then;
6
+
7
+ const eventType = firstWhen?.eventRef;
8
+ const commandType = firstThen?.commandRef;
9
+
10
+ const event = events.find(e => e.type === eventType);
11
+ const command = commands.find(c => c.type === commandType);
12
+
13
+ const eventImportPath = toKebabCase(event?.sourceSliceName ?? 'unknown');
14
+ const commandImportPath = toKebabCase(command?.sourceSliceName ?? 'unknown');
15
+ %>
16
+ import { describe, it, beforeEach } from 'vitest';
17
+ import 'reflect-metadata';
18
+ import {
19
+ getInMemoryEventStore,
20
+ type InMemoryEventStore,
21
+ type CommandSender,
22
+ } from '@event-driven-io/emmett';
23
+ import { type ReactorContext, ReactorSpecification } from '../../../shared';
24
+ import { react } from './react';
25
+ import type { <%= pascalCase(eventType) %> } from '../<%= eventImportPath %>/events';
26
+ import type { <%= pascalCase(commandType) %> } from '../<%= commandImportPath %>/commands';
27
+
28
+ describe('<%= pascalCase(flowName) %> | <%= pascalCase(slice.name) %>', () => {
29
+ let eventStore: InMemoryEventStore;
30
+ let given: ReactorSpecification<<%= pascalCase(eventType) %>, <%= pascalCase(commandType) %>, ReactorContext>;
31
+ let messageBus: CommandSender;
32
+
33
+ beforeEach(() => {
34
+ eventStore = getInMemoryEventStore({});
35
+ given = ReactorSpecification.for<<%= pascalCase(eventType) %>, <%= pascalCase(commandType) %>, ReactorContext>(
36
+ () => react({ eventStore, commandSender: messageBus }),
37
+ (commandSender) => {
38
+ messageBus = commandSender;
39
+ return {
40
+ eventStore,
41
+ commandSender,
42
+ database: eventStore.database,
43
+ };
44
+ }
45
+ );
46
+ });
47
+
48
+ <% for (const example of gwtList) {
49
+ const exampleEvent = Array.isArray(example.when) ? example.when[0] : example.when;
50
+ const commands = example.then;
51
+ const description = `should send ${commands.map(c => c.commandRef).join(', ')} when ${exampleEvent.eventRef} is received`;
52
+ %>
53
+ it('<%= description %>', async () => {
54
+ await given([])
55
+ .when({
56
+ type: '<%= exampleEvent.eventRef %>',
57
+ data: <%- formatDataObject(exampleEvent.exampleData, events.find(e => e.type === exampleEvent.eventRef)) %>
58
+ })
59
+ <% if (commands.length === 1) {
60
+ const commandSchema = commands[0];
61
+ %>
62
+ .then({
63
+ type: '<%= commandSchema.commandRef %>',
64
+ kind: 'Command',
65
+ data: <%- formatDataObject(commandSchema.exampleData, messages.find(m => m.name === commandSchema.commandRef && m.type === 'command')) %>
66
+ });
67
+ <% } else { %>
68
+ .then([
69
+ <% for (const cmd of commands) { %>
70
+ {
71
+ type: '<%= cmd.commandRef %>',
72
+ kind: 'Command',
73
+ data: <%- formatDataObject(cmd.exampleData, messages.find(m => m.name === cmd.commandRef && m.type === 'command')) %>
74
+ },
75
+ <% } %>
76
+ ]);
77
+ <% } %>
78
+ });
79
+ <% } %>
80
+ });
@@ -0,0 +1,56 @@
1
+ <%
2
+ const gwt = slice.server?.gwt?.[0];
3
+ const when = Array.isArray(gwt?.when) ? gwt.when[0] : gwt?.when;
4
+ const then = Array.isArray(gwt?.then) ? gwt.then[0] : gwt?.then;
5
+
6
+ const eventType = when?.eventRef;
7
+ const commandType = then?.commandRef;
8
+ const event = events.find(e => e.type === eventType);
9
+ -%>
10
+ import {
11
+ inMemoryReactor,
12
+ type MessageHandlerResult,
13
+ IllegalStateError,
14
+ } from '@event-driven-io/emmett';
15
+ import type { <%= pascalCase(eventType) %> } from '../<%= toKebabCase(event?.sourceSliceName ?? 'unknown') %>/events';
16
+ import type { ReactorContext } from '../../../shared';
17
+
18
+ export const react = ({ eventStore, commandSender }: ReactorContext) =>
19
+ inMemoryReactor<<%= pascalCase(eventType) %>>({
20
+ processorId: '<%= toKebabCase(flowName) %>-<%= toKebabCase(slice.name) %>',
21
+ canHandle: ['<%= eventType %>'],
22
+ connectionOptions: {
23
+ database: eventStore.database,
24
+ },
25
+ eachMessage: async (event, context): Promise<MessageHandlerResult> => {
26
+ /**
27
+ * ## IMPLEMENTATION INSTRUCTIONS ##
28
+ *
29
+ * - Inspect event data to determine if the command should be sent.
30
+ * - Replace the placeholder logic and \`throw\` below with real implementation.
31
+ * - Send one or more commands via: context.commandSender.send({...})
32
+ * - Optionally return a MessageHandlerResult for SKIP or error cases.
33
+ */
34
+
35
+ throw new IllegalStateError('Not yet implemented: react in response to <%= eventType %>');
36
+
37
+ // Example:
38
+ // if (event.data.status !== 'expected') {
39
+ // return {
40
+ // type: 'SKIP',
41
+ // reason: 'Condition not met',
42
+ // };
43
+ // }
44
+
45
+ // await context.commandSender.send({
46
+ // type: '<%= commandType %>',
47
+ // kind: 'Command',
48
+ // data: {
49
+ // // Map event fields to command fields here
50
+ // // e.g., userId: event.data.userId,
51
+ // },
52
+ // });
53
+
54
+ // return;
55
+ },
56
+ });