create-auto-app 0.1.0 → 0.1.2

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 (187) hide show
  1. package/dist/index.js +189 -85
  2. package/dist/index.js.map +1 -1
  3. package/package.json +1 -1
  4. package/templates/shopping-app/.context/auto-ia-scheme.json +175 -0
  5. package/templates/shopping-app/.context/design-system.md +118 -0
  6. package/templates/shopping-app/.context/figma-variables.json +14448 -0
  7. package/templates/shopping-app/.context/schema.graphql +46 -0
  8. package/templates/shopping-app/.context/schema.json +583 -0
  9. package/templates/shopping-app/.context/shadcn-filter.ts +31 -0
  10. package/templates/shopping-app/.env +8 -0
  11. package/templates/shopping-app/auto.config.ts +21 -0
  12. package/templates/shopping-app/client/auto-configure.ts +107 -0
  13. package/templates/shopping-app/client/codegen.ts +16 -0
  14. package/templates/shopping-app/client/components.json +20 -0
  15. package/templates/shopping-app/client/design-system-principles.md +19 -0
  16. package/templates/shopping-app/client/eslint.config.ts +57 -0
  17. package/templates/shopping-app/client/index.html +26 -0
  18. package/templates/shopping-app/client/package.json +100 -0
  19. package/templates/shopping-app/client/postcss.config.js +6 -0
  20. package/templates/shopping-app/client/public/favicon.ico +0 -0
  21. package/templates/shopping-app/client/schema.graphql +46 -0
  22. package/templates/shopping-app/client/src/App.css +42 -0
  23. package/templates/shopping-app/client/src/App.tsx +20 -0
  24. package/templates/shopping-app/client/src/apolloClient.ts +8 -0
  25. package/templates/shopping-app/client/src/components/atoms/accordion.tsx +51 -0
  26. package/templates/shopping-app/client/src/components/atoms/alert-dialog.tsx +111 -0
  27. package/templates/shopping-app/client/src/components/atoms/alert.tsx +49 -0
  28. package/templates/shopping-app/client/src/components/atoms/aspect-ratio.tsx +7 -0
  29. package/templates/shopping-app/client/src/components/atoms/avatar.tsx +32 -0
  30. package/templates/shopping-app/client/src/components/atoms/badge.tsx +36 -0
  31. package/templates/shopping-app/client/src/components/atoms/breadcrumb.tsx +92 -0
  32. package/templates/shopping-app/client/src/components/atoms/button.tsx +50 -0
  33. package/templates/shopping-app/client/src/components/atoms/calendar.tsx +156 -0
  34. package/templates/shopping-app/client/src/components/atoms/card.tsx +56 -0
  35. package/templates/shopping-app/client/src/components/atoms/carousel.tsx +214 -0
  36. package/templates/shopping-app/client/src/components/atoms/chart.tsx +290 -0
  37. package/templates/shopping-app/client/src/components/atoms/checkbox.tsx +27 -0
  38. package/templates/shopping-app/client/src/components/atoms/collapsible.tsx +15 -0
  39. package/templates/shopping-app/client/src/components/atoms/command.tsx +135 -0
  40. package/templates/shopping-app/client/src/components/atoms/context-menu.tsx +209 -0
  41. package/templates/shopping-app/client/src/components/atoms/dialog.tsx +123 -0
  42. package/templates/shopping-app/client/src/components/atoms/drawer.tsx +106 -0
  43. package/templates/shopping-app/client/src/components/atoms/dropdown-menu.tsx +217 -0
  44. package/templates/shopping-app/client/src/components/atoms/hover-card.tsx +36 -0
  45. package/templates/shopping-app/client/src/components/atoms/input-otp.tsx +66 -0
  46. package/templates/shopping-app/client/src/components/atoms/input.tsx +21 -0
  47. package/templates/shopping-app/client/src/components/atoms/label.tsx +19 -0
  48. package/templates/shopping-app/client/src/components/atoms/logo.tsx +33 -0
  49. package/templates/shopping-app/client/src/components/atoms/menubar.tsx +234 -0
  50. package/templates/shopping-app/client/src/components/atoms/navigation-menu.tsx +142 -0
  51. package/templates/shopping-app/client/src/components/atoms/pagination.tsx +100 -0
  52. package/templates/shopping-app/client/src/components/atoms/popover.tsx +40 -0
  53. package/templates/shopping-app/client/src/components/atoms/progress.tsx +22 -0
  54. package/templates/shopping-app/client/src/components/atoms/radio-group.tsx +31 -0
  55. package/templates/shopping-app/client/src/components/atoms/resizable.tsx +46 -0
  56. package/templates/shopping-app/client/src/components/atoms/scroll-area.tsx +46 -0
  57. package/templates/shopping-app/client/src/components/atoms/select.tsx +158 -0
  58. package/templates/shopping-app/client/src/components/atoms/separator.tsx +26 -0
  59. package/templates/shopping-app/client/src/components/atoms/sheet.tsx +101 -0
  60. package/templates/shopping-app/client/src/components/atoms/sidebar.tsx +675 -0
  61. package/templates/shopping-app/client/src/components/atoms/skeleton.tsx +7 -0
  62. package/templates/shopping-app/client/src/components/atoms/slider.tsx +54 -0
  63. package/templates/shopping-app/client/src/components/atoms/sonner.tsx +23 -0
  64. package/templates/shopping-app/client/src/components/atoms/switch.tsx +26 -0
  65. package/templates/shopping-app/client/src/components/atoms/table.tsx +73 -0
  66. package/templates/shopping-app/client/src/components/atoms/tabs.tsx +40 -0
  67. package/templates/shopping-app/client/src/components/atoms/textarea.tsx +18 -0
  68. package/templates/shopping-app/client/src/components/atoms/toggle-group.tsx +65 -0
  69. package/templates/shopping-app/client/src/components/atoms/toggle.tsx +39 -0
  70. package/templates/shopping-app/client/src/components/atoms/tooltip.tsx +48 -0
  71. package/templates/shopping-app/client/src/components/molecules/QuantitySelector.tsx +8 -0
  72. package/templates/shopping-app/client/src/components/molecules/ShoppingCriteriaForm.tsx +8 -0
  73. package/templates/shopping-app/client/src/components/molecules/SuggestedItemCard.tsx +10 -0
  74. package/templates/shopping-app/client/src/components/organisms/AssistantChatInterface.tsx +14 -0
  75. package/templates/shopping-app/client/src/components/organisms/PageHeader.tsx +6 -0
  76. package/templates/shopping-app/client/src/components/organisms/SuggestedItemsList.tsx +15 -0
  77. package/templates/shopping-app/client/src/gql/fragment-masking.ts +48 -0
  78. package/templates/shopping-app/client/src/gql/gql.ts +47 -0
  79. package/templates/shopping-app/client/src/gql/graphql.ts +106 -0
  80. package/templates/shopping-app/client/src/gql/index.ts +2 -0
  81. package/templates/shopping-app/client/src/graphql/mutations.ts +13 -0
  82. package/templates/shopping-app/client/src/graphql/queries.ts +14 -0
  83. package/templates/shopping-app/client/src/hooks/use-mobile.tsx +19 -0
  84. package/templates/shopping-app/client/src/hooks/use-toast.ts +186 -0
  85. package/templates/shopping-app/client/src/index.css +153 -0
  86. package/templates/shopping-app/client/src/lib/utils.ts +6 -0
  87. package/templates/shopping-app/client/src/main.tsx +5 -0
  88. package/templates/shopping-app/client/src/mockApolloClient.ts +93 -0
  89. package/templates/shopping-app/client/src/pages/AssistantChatPage.tsx +8 -0
  90. package/templates/shopping-app/client/src/pages/Index.tsx +10 -0
  91. package/templates/shopping-app/client/src/pages/NotFound.tsx +22 -0
  92. package/templates/shopping-app/client/src/pages/SuggestedItemsPage.tsx +8 -0
  93. package/templates/shopping-app/client/tailwind.config.ts +92 -0
  94. package/templates/shopping-app/client/tsconfig.json +49 -0
  95. package/templates/shopping-app/client/vite.config.ts +17 -0
  96. package/templates/shopping-app/flows/shopping-assistant.flow.ts +369 -0
  97. package/templates/shopping-app/package.json +25 -0
  98. package/templates/shopping-app/pnpm-workspace.yaml +2 -0
  99. package/templates/shopping-app/server/package.json +25 -0
  100. package/templates/shopping-app/server/scripts/generate-schema.ts +31 -0
  101. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/accepts-items-and-adds-to-their-cart/commands.ts +8 -0
  102. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/accepts-items-and-adds-to-their-cart/decide.specs.ts +46 -0
  103. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/accepts-items-and-adds-to-their-cart/decide.ts +36 -0
  104. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/accepts-items-and-adds-to-their-cart/events.ts +10 -0
  105. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/accepts-items-and-adds-to-their-cart/evolve.ts +28 -0
  106. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/accepts-items-and-adds-to-their-cart/handle.ts +31 -0
  107. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/accepts-items-and-adds-to-their-cart/mutation.resolver.ts +29 -0
  108. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/accepts-items-and-adds-to-their-cart/register.ts +10 -0
  109. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/accepts-items-and-adds-to-their-cart/state.ts +47 -0
  110. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/creates-a-chat-session-/react.specs.ts +63 -0
  111. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/creates-a-chat-session-/react.ts +49 -0
  112. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/creates-a-chat-session-/register.ts +31 -0
  113. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/enters-shopping-criteria-into-assistant/commands.ts +8 -0
  114. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/enters-shopping-criteria-into-assistant/decide.specs.ts +38 -0
  115. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/enters-shopping-criteria-into-assistant/decide.ts +36 -0
  116. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/enters-shopping-criteria-into-assistant/events.ts +10 -0
  117. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/enters-shopping-criteria-into-assistant/evolve.ts +28 -0
  118. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/enters-shopping-criteria-into-assistant/handle.ts +31 -0
  119. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/enters-shopping-criteria-into-assistant/mutation.resolver.ts +30 -0
  120. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/enters-shopping-criteria-into-assistant/register.ts +10 -0
  121. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/enters-shopping-criteria-into-assistant/state.ts +47 -0
  122. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/selects-items-relevant-to-the-shopping-criteria-/commands.ts +8 -0
  123. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/selects-items-relevant-to-the-shopping-criteria-/decide.specs.ts +61 -0
  124. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/selects-items-relevant-to-the-shopping-criteria-/decide.ts +45 -0
  125. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/selects-items-relevant-to-the-shopping-criteria-/events.ts +14 -0
  126. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/selects-items-relevant-to-the-shopping-criteria-/evolve.ts +28 -0
  127. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/selects-items-relevant-to-the-shopping-criteria-/handle.ts +59 -0
  128. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/selects-items-relevant-to-the-shopping-criteria-/mutation.resolver.ts +30 -0
  129. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/selects-items-relevant-to-the-shopping-criteria-/register.ts +10 -0
  130. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/selects-items-relevant-to-the-shopping-criteria-/state.ts +47 -0
  131. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/views-suggested-items/projection.specs.ts +94 -0
  132. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/views-suggested-items/projection.ts +40 -0
  133. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/views-suggested-items/query.resolver.ts +59 -0
  134. package/templates/shopping-app/server/src/domain/flows/seasonal-assistant/views-suggested-items/state.ts +9 -0
  135. package/templates/shopping-app/server/src/domain/shared/ReadModel.d.ts +10 -0
  136. package/templates/shopping-app/server/src/domain/shared/ReadModel.d.ts.map +1 -0
  137. package/templates/shopping-app/server/src/domain/shared/ReadModel.js +19 -0
  138. package/templates/shopping-app/server/src/domain/shared/ReadModel.js.map +1 -0
  139. package/templates/shopping-app/server/src/domain/shared/ReadModel.ts +26 -0
  140. package/templates/shopping-app/server/src/domain/shared/index.d.ts +5 -0
  141. package/templates/shopping-app/server/src/domain/shared/index.d.ts.map +1 -0
  142. package/templates/shopping-app/server/src/domain/shared/index.js +5 -0
  143. package/templates/shopping-app/server/src/domain/shared/index.js.map +1 -0
  144. package/templates/shopping-app/server/src/domain/shared/index.ts +4 -0
  145. package/templates/shopping-app/server/src/domain/shared/reactorSpecification.d.ts +35 -0
  146. package/templates/shopping-app/server/src/domain/shared/reactorSpecification.d.ts.map +1 -0
  147. package/templates/shopping-app/server/src/domain/shared/reactorSpecification.js +155 -0
  148. package/templates/shopping-app/server/src/domain/shared/reactorSpecification.js.map +1 -0
  149. package/templates/shopping-app/server/src/domain/shared/reactorSpecification.ts +257 -0
  150. package/templates/shopping-app/server/src/domain/shared/sendCommand.d.ts +4 -0
  151. package/templates/shopping-app/server/src/domain/shared/sendCommand.d.ts.map +1 -0
  152. package/templates/shopping-app/server/src/domain/shared/sendCommand.js +17 -0
  153. package/templates/shopping-app/server/src/domain/shared/sendCommand.js.map +1 -0
  154. package/templates/shopping-app/server/src/domain/shared/sendCommand.ts +21 -0
  155. package/templates/shopping-app/server/src/domain/shared/types.d.ts +19 -0
  156. package/templates/shopping-app/server/src/domain/shared/types.d.ts.map +1 -0
  157. package/templates/shopping-app/server/src/domain/shared/types.js +39 -0
  158. package/templates/shopping-app/server/src/domain/shared/types.js.map +1 -0
  159. package/templates/shopping-app/server/src/domain/shared/types.ts +31 -0
  160. package/templates/shopping-app/server/src/integrations/ai-integration.ts +76 -0
  161. package/templates/shopping-app/server/src/integrations/cart-integration.ts +178 -0
  162. package/templates/shopping-app/server/src/integrations/index.ts +3 -0
  163. package/templates/shopping-app/server/src/integrations/product-catalogue-integration.ts +363 -0
  164. package/templates/shopping-app/server/src/server.ts +43 -0
  165. package/templates/shopping-app/server/src/utils/index.d.ts +4 -0
  166. package/templates/shopping-app/server/src/utils/index.d.ts.map +1 -0
  167. package/templates/shopping-app/server/src/utils/index.js +4 -0
  168. package/templates/shopping-app/server/src/utils/index.js.map +1 -0
  169. package/templates/shopping-app/server/src/utils/index.ts +3 -0
  170. package/templates/shopping-app/server/src/utils/loadProjections.d.ts +3 -0
  171. package/templates/shopping-app/server/src/utils/loadProjections.d.ts.map +1 -0
  172. package/templates/shopping-app/server/src/utils/loadProjections.js +23 -0
  173. package/templates/shopping-app/server/src/utils/loadProjections.js.map +1 -0
  174. package/templates/shopping-app/server/src/utils/loadProjections.ts +30 -0
  175. package/templates/shopping-app/server/src/utils/loadRegisterFiles.d.ts +6 -0
  176. package/templates/shopping-app/server/src/utils/loadRegisterFiles.d.ts.map +1 -0
  177. package/templates/shopping-app/server/src/utils/loadRegisterFiles.js +28 -0
  178. package/templates/shopping-app/server/src/utils/loadRegisterFiles.js.map +1 -0
  179. package/templates/shopping-app/server/src/utils/loadRegisterFiles.ts +41 -0
  180. package/templates/shopping-app/server/src/utils/loadResolvers.d.ts +5 -0
  181. package/templates/shopping-app/server/src/utils/loadResolvers.d.ts.map +1 -0
  182. package/templates/shopping-app/server/src/utils/loadResolvers.js +27 -0
  183. package/templates/shopping-app/server/src/utils/loadResolvers.js.map +1 -0
  184. package/templates/shopping-app/server/src/utils/loadResolvers.ts +36 -0
  185. package/templates/shopping-app/server/tsconfig.json +19 -0
  186. package/templates/shopping-app/server/vitest.config.ts +7 -0
  187. package/templates/shopping-app/tsconfig.json +21 -0
@@ -0,0 +1,178 @@
1
+ import type { State, Integration } from '@auto-engineer/flowlang';
2
+ import axios from 'axios';
3
+ import { z } from '@auto-engineer/ai-gateway';
4
+
5
+ export const CartItemSchema = z.object({
6
+ productId: z.string(),
7
+ name: z.string(),
8
+ price: z.number(),
9
+ quantity: z.number(),
10
+ });
11
+
12
+ export const CartSessionSchema = z.object({
13
+ sessionId: z.string(),
14
+ items: z.array(CartItemSchema),
15
+ total: z.number(),
16
+ createdAt: z.string(),
17
+ updatedAt: z.string(),
18
+ });
19
+
20
+ export type CartItem = {
21
+ productId: string;
22
+ name: string;
23
+ price: number;
24
+ quantity: number;
25
+ };
26
+
27
+ export type CartSession = {
28
+ sessionId: string;
29
+ items: CartItem[];
30
+ total: number;
31
+ createdAt: string;
32
+ updatedAt: string;
33
+ };
34
+
35
+ export type CartState = State<'Cart', CartSession>;
36
+
37
+ export type CartAddedState = State<'CartAdded', CartSession>;
38
+
39
+ export type CartRemovedState = State<'CartRemoved', CartSession>;
40
+
41
+ export type CartClearedState = State<'CartCleared', CartSession>;
42
+
43
+ const client = axios.create({
44
+ baseURL: 'http://localhost:3002',
45
+ timeout: 10000,
46
+ headers: {
47
+ 'Content-Type': 'application/json',
48
+ },
49
+ });
50
+
51
+ type CartQueries = {
52
+ Cart: (params: { sessionId: string }) => Promise<CartState>;
53
+ };
54
+
55
+ type CartCommands = {
56
+ AddToCart: (params: {
57
+ sessionId: string;
58
+ productId: string;
59
+ name: string;
60
+ price: number;
61
+ quantity: number;
62
+ }) => Promise<CartAddedState>;
63
+ RemoveFromCart: (params: { sessionId: string; productId: string; quantity?: number }) => Promise<CartRemovedState>;
64
+ ClearCart: (params: { sessionId: string }) => Promise<CartClearedState>;
65
+ };
66
+
67
+ export const Cart: Integration<'cart', CartQueries, CartCommands> = {
68
+ __brand: 'Integration' as const,
69
+ type: 'cart' as const,
70
+ name: 'cart',
71
+ Queries: {
72
+ schema: {
73
+ Cart: z.object({
74
+ sessionId: z.string(),
75
+ }),
76
+ },
77
+ Cart: async (params: { sessionId: string }): Promise<CartState> => {
78
+ try {
79
+ const cartSession = (await client.get<CartSession>(`/api/cart/${params.sessionId}`)).data;
80
+ return {
81
+ type: 'Cart',
82
+ data: cartSession,
83
+ };
84
+ } catch (error) {
85
+ console.error(`Failed to fetch cart for session ${params.sessionId}:`, error);
86
+ return {
87
+ type: 'Cart',
88
+ data: {
89
+ sessionId: params.sessionId,
90
+ items: [],
91
+ total: 0,
92
+ createdAt: new Date().toISOString(),
93
+ updatedAt: new Date().toISOString(),
94
+ },
95
+ };
96
+ }
97
+ },
98
+ },
99
+ Commands: {
100
+ schema: {
101
+ AddToCart: z.object({
102
+ sessionId: z.string(),
103
+ productId: z.string(),
104
+ name: z.string(),
105
+ price: z.number(),
106
+ quantity: z.number(),
107
+ }),
108
+ RemoveFromCart: z.object({
109
+ sessionId: z.string(),
110
+ productId: z.string(),
111
+ quantity: z.number().optional(),
112
+ }),
113
+ ClearCart: z.object({
114
+ sessionId: z.string(),
115
+ }),
116
+ },
117
+ AddToCart: async (params: {
118
+ sessionId: string;
119
+ productId: string;
120
+ name: string;
121
+ price: number;
122
+ quantity: number;
123
+ }): Promise<CartAddedState> => {
124
+ try {
125
+ const cartSession = (
126
+ await client.post<CartSession>(`/api/cart/${params.sessionId}/add`, {
127
+ productId: params.productId,
128
+ name: params.name,
129
+ price: params.price,
130
+ quantity: params.quantity,
131
+ })
132
+ ).data;
133
+ return {
134
+ type: 'CartAdded',
135
+ data: cartSession,
136
+ };
137
+ } catch (error) {
138
+ console.error(`Failed to add item to cart for session ${params.sessionId}:`, error);
139
+ throw error;
140
+ }
141
+ },
142
+ RemoveFromCart: async (params: {
143
+ sessionId: string;
144
+ productId: string;
145
+ quantity?: number;
146
+ }): Promise<CartRemovedState> => {
147
+ try {
148
+ const requestBody: { productId: string; quantity?: number } = {
149
+ productId: params.productId,
150
+ };
151
+ if (params.quantity !== undefined) {
152
+ requestBody.quantity = params.quantity;
153
+ }
154
+
155
+ const cartSession = (await client.post<CartSession>(`/api/cart/${params.sessionId}/remove`, requestBody)).data;
156
+ return {
157
+ type: 'CartRemoved',
158
+ data: cartSession,
159
+ };
160
+ } catch (error) {
161
+ console.error(`Failed to remove item from cart for session ${params.sessionId}:`, error);
162
+ throw error;
163
+ }
164
+ },
165
+ ClearCart: async (params: { sessionId: string }): Promise<CartClearedState> => {
166
+ try {
167
+ const cartSession = (await client.post<CartSession>(`/api/cart/${params.sessionId}/clear`)).data;
168
+ return {
169
+ type: 'CartCleared',
170
+ data: cartSession,
171
+ };
172
+ } catch (error) {
173
+ console.error(`Failed to clear cart for session ${params.sessionId}:`, error);
174
+ throw error;
175
+ }
176
+ },
177
+ },
178
+ };
@@ -0,0 +1,3 @@
1
+ export * from './ai-integration';
2
+ export * from './cart-integration';
3
+ export * from './product-catalogue-integration';
@@ -0,0 +1,363 @@
1
+ import type { State, Integration } from '@auto-engineer/flowlang';
2
+ import axios from 'axios';
3
+ import { registerTool, z } from '@auto-engineer/ai-gateway';
4
+
5
+ export const ProductSchema = z.object({
6
+ productId: z.string(),
7
+ name: z.string(),
8
+ category: z.string(),
9
+ price: z.number(),
10
+ tags: z.array(z.string()),
11
+ imageUrl: z.string().url(),
12
+ });
13
+
14
+ const ProductsSchema = z.object({
15
+ type: z.literal('Products'),
16
+ data: z.object({
17
+ products: z.array(ProductSchema),
18
+ }),
19
+ });
20
+
21
+ export type Product = {
22
+ productId: string;
23
+ name: string;
24
+ category: string;
25
+ price: number;
26
+ tags: string[];
27
+ imageUrl: string;
28
+ };
29
+
30
+ export type Products = State<
31
+ 'Products',
32
+ {
33
+ products: Product[];
34
+ }
35
+ >;
36
+
37
+ export type ProductsByCategory = State<
38
+ 'ProductsByCategory',
39
+ {
40
+ category: string;
41
+ products: Product[];
42
+ }
43
+ >;
44
+
45
+ export type ProductSearchResults = State<
46
+ 'ProductSearchResults',
47
+ {
48
+ query: string;
49
+ products: Product[];
50
+ }
51
+ >;
52
+
53
+ export type ProductDetails = State<
54
+ 'ProductDetails',
55
+ {
56
+ product: Product | null;
57
+ }
58
+ >;
59
+
60
+ const client = axios.create({
61
+ baseURL: 'http://localhost:3001',
62
+ timeout: 10000,
63
+ headers: {
64
+ 'Content-Type': 'application/json',
65
+ },
66
+ });
67
+
68
+ type ProductCatalogQueries = {
69
+ Products: () => Promise<Products>;
70
+ ProductsByCategory: (params: { category: string }) => Promise<ProductsByCategory>;
71
+ ProductSearchResults: (params: { query: string }) => Promise<ProductSearchResults>;
72
+ ProductDetails: (params: { id: string }) => Promise<ProductDetails>;
73
+ };
74
+
75
+ export const ProductCatalog: Integration<'product-catalog', ProductCatalogQueries> = {
76
+ __brand: 'Integration' as const,
77
+ type: 'product-catalog' as const,
78
+ name: 'product-catalog',
79
+ Queries: {
80
+ schema: {
81
+ Products: ProductsSchema,
82
+ // ProductsByCategory: z.object({
83
+ // category: z.string(),
84
+ // products: z.array(ProductSchema),
85
+ // }),
86
+ // ProductSearchResults: z.object({
87
+ // query: z.string(),
88
+ // products: z.array(ProductSchema),
89
+ // }),
90
+ // ProductDetails: z.object({
91
+ // product: ProductSchema.nullable(),
92
+ // }),
93
+ },
94
+ Products: async (): Promise<Products> => {
95
+ try {
96
+ const products = (await client.get<Product[]>('/api/products')).data;
97
+ return {
98
+ type: 'Products',
99
+ data: {
100
+ products,
101
+ },
102
+ };
103
+ } catch (error) {
104
+ console.error('Failed to fetch products:', error);
105
+ return {
106
+ type: 'Products',
107
+ data: {
108
+ products: [],
109
+ },
110
+ };
111
+ }
112
+ },
113
+ ProductsByCategory: async (params: { category: string }): Promise<ProductsByCategory> => {
114
+ try {
115
+ const products = (await client.get<Product[]>(`/api/products/category/${params.category}`)).data;
116
+ return {
117
+ type: 'ProductsByCategory',
118
+ data: {
119
+ category: params.category,
120
+ products,
121
+ },
122
+ };
123
+ } catch (error) {
124
+ console.error(`Failed to fetch products for category ${params.category}:`, error);
125
+ return {
126
+ type: 'ProductsByCategory',
127
+ data: {
128
+ category: params.category,
129
+ products: [],
130
+ },
131
+ };
132
+ }
133
+ },
134
+ ProductSearchResults: async (params: { query: string }): Promise<ProductSearchResults> => {
135
+ try {
136
+ const products = (
137
+ await client.get<Product[]>('/api/products/search', {
138
+ params: { q: params.query },
139
+ })
140
+ ).data;
141
+ return {
142
+ type: 'ProductSearchResults',
143
+ data: {
144
+ query: params.query,
145
+ products,
146
+ },
147
+ };
148
+ } catch (error) {
149
+ console.error(`Failed to search products with query "${params.query}":`, error);
150
+ return {
151
+ type: 'ProductSearchResults',
152
+ data: {
153
+ query: params.query,
154
+ products: [],
155
+ },
156
+ };
157
+ }
158
+ },
159
+ ProductDetails: async (params: { id: string }): Promise<ProductDetails> => {
160
+ try {
161
+ const product = (await client.get<Product>(`/api/products/${params.id}`)).data;
162
+ return {
163
+ type: 'ProductDetails',
164
+ data: {
165
+ product,
166
+ },
167
+ };
168
+ } catch (error) {
169
+ if (axios.isAxiosError(error) && error.response?.status === 404) {
170
+ return {
171
+ type: 'ProductDetails',
172
+ data: {
173
+ product: null,
174
+ },
175
+ };
176
+ }
177
+ console.error(`Failed to fetch product details for ID ${params.id}:`, error);
178
+ return {
179
+ type: 'ProductDetails',
180
+ data: {
181
+ product: null,
182
+ },
183
+ };
184
+ }
185
+ },
186
+ },
187
+ };
188
+
189
+ // Register MCP tools for ProductCatalog queries
190
+
191
+ // Type definitions for query functions
192
+ type ProductsQuery = () => Promise<Products>;
193
+ type ProductsByCategoryQuery = (params: { category: string }) => Promise<ProductsByCategory>;
194
+ type ProductSearchQuery = (params: { query: string }) => Promise<ProductSearchResults>;
195
+ type ProductDetailsQuery = (params: { id: string }) => Promise<ProductDetails>;
196
+
197
+ // Tool for fetching all products
198
+ registerTool<Record<string, unknown>>(
199
+ 'PRODUCT_CATALOGUE_PRODUCTS',
200
+ {
201
+ title: 'Get All Products',
202
+ description: 'Fetches all products from the product catalog',
203
+ inputSchema: {},
204
+ schema: ProductsSchema,
205
+ schemaName: 'Products',
206
+ schemaDescription: 'A list of products with id, name, category, price, tags, and imageUrl',
207
+ },
208
+ async () => {
209
+ const queries = ProductCatalog.Queries;
210
+ if (!queries?.Products) {
211
+ return {
212
+ content: [
213
+ {
214
+ type: 'text' as const,
215
+ text: 'ProductCatalog.Queries.Products is not available',
216
+ },
217
+ ],
218
+ isError: true,
219
+ };
220
+ }
221
+ const productsQuery = queries.Products as ProductsQuery;
222
+ const result = await productsQuery();
223
+ return {
224
+ content: [
225
+ {
226
+ type: 'text' as const,
227
+ text: JSON.stringify(result.data, null, 2),
228
+ },
229
+ ],
230
+ };
231
+ },
232
+ );
233
+
234
+ // Tool for fetching products by category
235
+ interface ProductsByCategoryParams extends Record<string, unknown> {
236
+ category: string;
237
+ }
238
+
239
+ registerTool<ProductsByCategoryParams>(
240
+ 'PRODUCT_CATALOGUE_PRODUCTS_BY_CATEGORY',
241
+ {
242
+ title: 'Get Products by Category',
243
+ description: 'Fetches products from a specific category',
244
+ inputSchema: {
245
+ category: z.string().min(1, 'Category is required'),
246
+ },
247
+ },
248
+ async ({ category }) => {
249
+ const queries = ProductCatalog.Queries;
250
+ if (!queries?.ProductsByCategory) {
251
+ return {
252
+ content: [
253
+ {
254
+ type: 'text' as const,
255
+ text: 'ProductCatalog.Queries.ProductsByCategory is not available',
256
+ },
257
+ ],
258
+ isError: true,
259
+ };
260
+ }
261
+ const categoryQuery = queries.ProductsByCategory as ProductsByCategoryQuery;
262
+ const result = await categoryQuery({ category });
263
+ return {
264
+ content: [
265
+ {
266
+ type: 'text' as const,
267
+ text: JSON.stringify(result.data, null, 2),
268
+ },
269
+ ],
270
+ };
271
+ },
272
+ );
273
+
274
+ // Tool for searching products
275
+ interface ProductSearchParams extends Record<string, unknown> {
276
+ query: string;
277
+ }
278
+
279
+ registerTool<ProductSearchParams>(
280
+ 'PRODUCT_CATALOGUE_SEARCH',
281
+ {
282
+ title: 'Search Products',
283
+ description: 'Search for products using a query string',
284
+ inputSchema: {
285
+ query: z.string().min(1, 'Search query is required'),
286
+ },
287
+ },
288
+ async ({ query }) => {
289
+ const queries = ProductCatalog.Queries;
290
+ if (!queries?.ProductSearchResults) {
291
+ return {
292
+ content: [
293
+ {
294
+ type: 'text' as const,
295
+ text: 'ProductCatalog.Queries.ProductSearchResults is not available',
296
+ },
297
+ ],
298
+ isError: true,
299
+ };
300
+ }
301
+ const searchQuery = queries.ProductSearchResults as ProductSearchQuery;
302
+ const result = await searchQuery({ query });
303
+ return {
304
+ content: [
305
+ {
306
+ type: 'text' as const,
307
+ text: JSON.stringify(result.data, null, 2),
308
+ },
309
+ ],
310
+ };
311
+ },
312
+ );
313
+
314
+ // Tool for getting product details
315
+ interface ProductDetailsParams extends Record<string, unknown> {
316
+ id: string;
317
+ }
318
+
319
+ registerTool<ProductDetailsParams>(
320
+ 'PRODUCT_CATALOGUE_PRODUCT_DETAILS',
321
+ {
322
+ title: 'Get Product Details',
323
+ description: 'Fetches detailed information about a specific product',
324
+ inputSchema: {
325
+ id: z.string().min(1, 'Product ID is required'),
326
+ },
327
+ },
328
+ async ({ id }) => {
329
+ const queries = ProductCatalog.Queries;
330
+ if (!queries?.ProductDetails) {
331
+ return {
332
+ content: [
333
+ {
334
+ type: 'text' as const,
335
+ text: 'ProductCatalog.Queries.ProductDetails is not available',
336
+ },
337
+ ],
338
+ isError: true,
339
+ };
340
+ }
341
+ const detailsQuery = queries.ProductDetails as ProductDetailsQuery;
342
+ const result = await detailsQuery({ id });
343
+ if (result.data.product === null) {
344
+ return {
345
+ content: [
346
+ {
347
+ type: 'text' as const,
348
+ text: `Product with ID "${id}" not found`,
349
+ },
350
+ ],
351
+ isError: true,
352
+ };
353
+ }
354
+ return {
355
+ content: [
356
+ {
357
+ type: 'text' as const,
358
+ text: JSON.stringify(result.data, null, 2),
359
+ },
360
+ ],
361
+ };
362
+ },
363
+ );
@@ -0,0 +1,43 @@
1
+ import 'reflect-metadata';
2
+ import { ApolloServer } from 'apollo-server';
3
+ import { buildSchema } from 'type-graphql';
4
+ import { loadProjections, loadRegisterFiles, loadResolvers } from './utils';
5
+ import {
6
+ getInMemoryEventStore,
7
+ getInMemoryMessageBus,
8
+ projections,
9
+ forwardToMessageBus,
10
+ } from '@event-driven-io/emmett';
11
+
12
+ async function start() {
13
+ const loadedProjections = await loadProjections('src/domain/flows/**/projection.{ts,js}');
14
+ const registrations = await loadRegisterFiles('src/domain/flows/**/register.{ts,js}');
15
+
16
+ const messageBus = getInMemoryMessageBus();
17
+
18
+ const eventStore = getInMemoryEventStore({
19
+ projections: projections.inline(loadedProjections),
20
+ hooks: {
21
+ onAfterCommit: forwardToMessageBus(messageBus),
22
+ },
23
+ });
24
+
25
+ await Promise.all(registrations.map((r) => r.register(messageBus, eventStore)));
26
+
27
+ const resolvers = await loadResolvers('src/domain/flows/**/*.resolver.{ts,js}');
28
+ const schema = await buildSchema({
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
+ resolvers: resolvers as unknown as [(...args: any[]) => any, ...Array<(...args: any[]) => any>],
31
+ });
32
+ const server = new ApolloServer({
33
+ schema,
34
+ context: () => ({
35
+ eventStore,
36
+ messageBus,
37
+ }),
38
+ });
39
+ const { url } = await server.listen({ port: 4000 });
40
+ console.log(`🚀 GraphQL server ready at ${url}`);
41
+ }
42
+
43
+ void start();
@@ -0,0 +1,4 @@
1
+ export * from './loadResolvers';
2
+ export * from './loadRegisterFiles';
3
+ export * from './loadProjections';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export * from './loadResolvers.js';
2
+ export * from './loadRegisterFiles.js';
3
+ export * from './loadProjections.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './loadResolvers';
2
+ export * from './loadRegisterFiles';
3
+ export * from './loadProjections';
@@ -0,0 +1,3 @@
1
+ import type { ProjectionDefinition } from '@event-driven-io/emmett';
2
+ export declare function loadProjections(source: string): Promise<ProjectionDefinition[]>;
3
+ //# sourceMappingURL=loadProjections.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadProjections.d.ts","sourceRoot":"","sources":["../../src/utils/loadProjections.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAEpE,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAoBrF"}
@@ -0,0 +1,23 @@
1
+ import fg from 'fast-glob';
2
+ import path from 'path';
3
+ export async function loadProjections(source) {
4
+ const files = await fg(source, { absolute: true });
5
+ const modules = await Promise.all(files.map(async (file) => {
6
+ const mod = await import(pathToFileUrl(file).href);
7
+ if (typeof mod === 'object' && mod !== null && 'projection' in mod) {
8
+ const projectionModule = mod;
9
+ if (projectionModule.projection == null) {
10
+ console.warn(`⚠️ Projection file "${file}" does not export "projection"`);
11
+ }
12
+ return projectionModule.projection;
13
+ }
14
+ console.warn(`⚠️ Projection file "${file}" does not export "projection"`);
15
+ return undefined;
16
+ }));
17
+ return modules.filter((p) => p != null);
18
+ }
19
+ function pathToFileUrl(filePath) {
20
+ const resolved = path.resolve(filePath);
21
+ return new URL(`file://${resolved}`);
22
+ }
23
+ //# sourceMappingURL=loadProjections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadProjections.js","sourceRoot":"","sources":["../../src/utils/loadProjections.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,WAAW,CAAC;AAC3B,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAc;IAClD,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAEnD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACvB,MAAM,GAAG,GAAY,MAAM,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;QAE5D,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,YAAY,IAAI,GAAG,EAAE,CAAC;YACnE,MAAM,gBAAgB,GAAG,GAA4C,CAAC;YACtE,IAAI,gBAAgB,CAAC,UAAU,IAAI,IAAI,EAAE,CAAC;gBACxC,OAAO,CAAC,IAAI,CAAC,uBAAuB,IAAI,gCAAgC,CAAC,CAAC;YAC5E,CAAC;YACD,OAAO,gBAAgB,CAAC,UAAU,CAAC;QACrC,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,uBAAuB,IAAI,gCAAgC,CAAC,CAAC;QAC1E,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC,CACH,CAAC;IAEF,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAA6B,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;AACrE,CAAC;AAED,SAAS,aAAa,CAAC,QAAgB;IACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxC,OAAO,IAAI,GAAG,CAAC,UAAU,QAAQ,EAAE,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,30 @@
1
+ import fg from 'fast-glob';
2
+ import path from 'path';
3
+ import type { ProjectionDefinition } from '@event-driven-io/emmett';
4
+
5
+ export async function loadProjections(source: string): Promise<ProjectionDefinition[]> {
6
+ const files = await fg(source, { absolute: true });
7
+
8
+ const modules = await Promise.all(
9
+ files.map(async (file) => {
10
+ const mod: unknown = await import(pathToFileUrl(file).href);
11
+
12
+ if (typeof mod === 'object' && mod !== null && 'projection' in mod) {
13
+ const projectionModule = mod as { projection?: ProjectionDefinition };
14
+ if (projectionModule.projection == null) {
15
+ console.warn(`⚠️ Projection file "${file}" does not export "projection"`);
16
+ }
17
+ return projectionModule.projection;
18
+ }
19
+ console.warn(`⚠️ Projection file "${file}" does not export "projection"`);
20
+ return undefined;
21
+ }),
22
+ );
23
+
24
+ return modules.filter((p): p is ProjectionDefinition => p != null);
25
+ }
26
+
27
+ function pathToFileUrl(filePath: string): URL {
28
+ const resolved = path.resolve(filePath);
29
+ return new URL(`file://${resolved}`);
30
+ }
@@ -0,0 +1,6 @@
1
+ import type { CommandProcessor, EventStore } from '@event-driven-io/emmett';
2
+ export interface SliceRegistration {
3
+ register: (messageBus: CommandProcessor, eventStore: EventStore) => Promise<unknown> | void;
4
+ }
5
+ export declare function loadRegisterFiles(source: string): Promise<SliceRegistration[]>;
6
+ //# sourceMappingURL=loadRegisterFiles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadRegisterFiles.d.ts","sourceRoot":"","sources":["../../src/utils/loadRegisterFiles.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE5E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,CAAC,UAAU,EAAE,gBAAgB,EAAE,UAAU,EAAE,UAAU,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;CAC7F;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CA2BpF"}