create-aws-project 1.2.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 (181) hide show
  1. package/README.md +118 -0
  2. package/dist/__tests__/generator/replace-tokens.spec.d.ts +1 -0
  3. package/dist/__tests__/generator/replace-tokens.spec.js +281 -0
  4. package/dist/__tests__/generator.spec.d.ts +1 -0
  5. package/dist/__tests__/generator.spec.js +162 -0
  6. package/dist/__tests__/validation/project-name.spec.d.ts +1 -0
  7. package/dist/__tests__/validation/project-name.spec.js +57 -0
  8. package/dist/__tests__/wizard.spec.d.ts +1 -0
  9. package/dist/__tests__/wizard.spec.js +232 -0
  10. package/dist/aws/iam.d.ts +75 -0
  11. package/dist/aws/iam.js +264 -0
  12. package/dist/aws/organizations.d.ts +79 -0
  13. package/dist/aws/organizations.js +168 -0
  14. package/dist/cli.d.ts +4 -0
  15. package/dist/cli.js +206 -0
  16. package/dist/commands/setup-github.d.ts +4 -0
  17. package/dist/commands/setup-github.js +185 -0
  18. package/dist/generator/copy-file.d.ts +15 -0
  19. package/dist/generator/copy-file.js +56 -0
  20. package/dist/generator/generate-project.d.ts +14 -0
  21. package/dist/generator/generate-project.js +81 -0
  22. package/dist/generator/index.d.ts +4 -0
  23. package/dist/generator/index.js +3 -0
  24. package/dist/generator/replace-tokens.d.ts +29 -0
  25. package/dist/generator/replace-tokens.js +68 -0
  26. package/dist/github/secrets.d.ts +109 -0
  27. package/dist/github/secrets.js +275 -0
  28. package/dist/index.d.ts +2 -0
  29. package/dist/index.js +6 -0
  30. package/dist/prompts/auth.d.ts +3 -0
  31. package/dist/prompts/auth.js +23 -0
  32. package/dist/prompts/aws-config.d.ts +2 -0
  33. package/dist/prompts/aws-config.js +14 -0
  34. package/dist/prompts/features.d.ts +2 -0
  35. package/dist/prompts/features.js +10 -0
  36. package/dist/prompts/github-setup.d.ts +53 -0
  37. package/dist/prompts/github-setup.js +208 -0
  38. package/dist/prompts/org-structure.d.ts +9 -0
  39. package/dist/prompts/org-structure.js +93 -0
  40. package/dist/prompts/platforms.d.ts +2 -0
  41. package/dist/prompts/platforms.js +12 -0
  42. package/dist/prompts/project-name.d.ts +2 -0
  43. package/dist/prompts/project-name.js +8 -0
  44. package/dist/prompts/theme.d.ts +2 -0
  45. package/dist/prompts/theme.js +14 -0
  46. package/dist/templates/index.d.ts +4 -0
  47. package/dist/templates/index.js +2 -0
  48. package/dist/templates/manifest.d.ts +11 -0
  49. package/dist/templates/manifest.js +99 -0
  50. package/dist/templates/tokens.d.ts +39 -0
  51. package/dist/templates/tokens.js +37 -0
  52. package/dist/templates/types.d.ts +52 -0
  53. package/dist/templates/types.js +1 -0
  54. package/dist/types.d.ts +27 -0
  55. package/dist/types.js +1 -0
  56. package/dist/validation/project-name.d.ts +1 -0
  57. package/dist/validation/project-name.js +12 -0
  58. package/dist/wizard.d.ts +2 -0
  59. package/dist/wizard.js +81 -0
  60. package/package.json +68 -0
  61. package/templates/.github/actions/build-and-test/action.yml +24 -0
  62. package/templates/.github/actions/deploy-cdk/action.yml +46 -0
  63. package/templates/.github/actions/deploy-web/action.yml +72 -0
  64. package/templates/.github/actions/setup/action.yml +29 -0
  65. package/templates/.github/pull_request_template.md +15 -0
  66. package/templates/.github/workflows/deploy-dev.yml +80 -0
  67. package/templates/.github/workflows/deploy-prod.yml +67 -0
  68. package/templates/.github/workflows/deploy-stage.yml +77 -0
  69. package/templates/.github/workflows/pull-request.yml +72 -0
  70. package/templates/.vscode/extensions.json +7 -0
  71. package/templates/.vscode/settings.json +67 -0
  72. package/templates/apps/api/.eslintrc.json +18 -0
  73. package/templates/apps/api/cdk/app.ts +93 -0
  74. package/templates/apps/api/cdk/auth/cognito-stack.ts +164 -0
  75. package/templates/apps/api/cdk/cdk.json +73 -0
  76. package/templates/apps/api/cdk/deployment-user-stack.ts +187 -0
  77. package/templates/apps/api/cdk/org-stack.ts +67 -0
  78. package/templates/apps/api/cdk/static-stack.ts +361 -0
  79. package/templates/apps/api/cdk/tsconfig.json +39 -0
  80. package/templates/apps/api/cdk/user-stack.ts +255 -0
  81. package/templates/apps/api/jest.config.ts +38 -0
  82. package/templates/apps/api/lambdas.yml +84 -0
  83. package/templates/apps/api/project.json.template +58 -0
  84. package/templates/apps/api/src/__tests__/setup.ts +10 -0
  85. package/templates/apps/api/src/handlers/users/create-user.ts +52 -0
  86. package/templates/apps/api/src/handlers/users/delete-user.ts +45 -0
  87. package/templates/apps/api/src/handlers/users/get-me.ts +72 -0
  88. package/templates/apps/api/src/handlers/users/get-user.ts +45 -0
  89. package/templates/apps/api/src/handlers/users/get-users.ts +23 -0
  90. package/templates/apps/api/src/handlers/users/index.ts +17 -0
  91. package/templates/apps/api/src/handlers/users/update-user.ts +72 -0
  92. package/templates/apps/api/src/lib/dynamo/dynamo-model.ts +504 -0
  93. package/templates/apps/api/src/lib/dynamo/index.ts +12 -0
  94. package/templates/apps/api/src/lib/dynamo/utils.ts +39 -0
  95. package/templates/apps/api/src/middleware/auth0-auth.ts +97 -0
  96. package/templates/apps/api/src/middleware/cognito-auth.ts +90 -0
  97. package/templates/apps/api/src/models/UserModel.ts +109 -0
  98. package/templates/apps/api/src/schemas/user.schema.ts +44 -0
  99. package/templates/apps/api/src/services/user-service.ts +108 -0
  100. package/templates/apps/api/src/utils/auth-context.ts +60 -0
  101. package/templates/apps/api/src/utils/common/helpers.ts +26 -0
  102. package/templates/apps/api/src/utils/lambda-handler.ts +148 -0
  103. package/templates/apps/api/src/utils/response.ts +52 -0
  104. package/templates/apps/api/src/utils/validator.ts +75 -0
  105. package/templates/apps/api/tsconfig.app.json +15 -0
  106. package/templates/apps/api/tsconfig.json +19 -0
  107. package/templates/apps/api/tsconfig.spec.json +17 -0
  108. package/templates/apps/mobile/.env.example +5 -0
  109. package/templates/apps/mobile/.eslintrc.json +33 -0
  110. package/templates/apps/mobile/app.json +33 -0
  111. package/templates/apps/mobile/assets/.gitkeep +0 -0
  112. package/templates/apps/mobile/babel.config.js +19 -0
  113. package/templates/apps/mobile/index.js +7 -0
  114. package/templates/apps/mobile/jest.config.ts +22 -0
  115. package/templates/apps/mobile/metro.config.js +35 -0
  116. package/templates/apps/mobile/package.json +22 -0
  117. package/templates/apps/mobile/project.json.template +64 -0
  118. package/templates/apps/mobile/src/App.tsx +367 -0
  119. package/templates/apps/mobile/src/__tests__/App.spec.tsx +46 -0
  120. package/templates/apps/mobile/src/__tests__/store/user-store.spec.ts +156 -0
  121. package/templates/apps/mobile/src/config/api.ts +16 -0
  122. package/templates/apps/mobile/src/store/user-store.ts +56 -0
  123. package/templates/apps/mobile/src/test-setup.ts +10 -0
  124. package/templates/apps/mobile/tsconfig.json +22 -0
  125. package/templates/apps/web/.env.example +13 -0
  126. package/templates/apps/web/.eslintrc.json +26 -0
  127. package/templates/apps/web/index.html +13 -0
  128. package/templates/apps/web/jest.config.ts +24 -0
  129. package/templates/apps/web/package.json +15 -0
  130. package/templates/apps/web/project.json.template +66 -0
  131. package/templates/apps/web/src/App.tsx +352 -0
  132. package/templates/apps/web/src/__mocks__/config/api.ts +41 -0
  133. package/templates/apps/web/src/__tests__/App.spec.tsx +240 -0
  134. package/templates/apps/web/src/__tests__/store/user-store.spec.ts +185 -0
  135. package/templates/apps/web/src/auth/auth0-provider.tsx +103 -0
  136. package/templates/apps/web/src/auth/cognito-provider.tsx +143 -0
  137. package/templates/apps/web/src/auth/index.ts +7 -0
  138. package/templates/apps/web/src/auth/use-auth.ts +16 -0
  139. package/templates/apps/web/src/config/amplify-config.ts +31 -0
  140. package/templates/apps/web/src/config/api.ts +38 -0
  141. package/templates/apps/web/src/config/auth0-config.ts +17 -0
  142. package/templates/apps/web/src/main.tsx +41 -0
  143. package/templates/apps/web/src/store/user-store.ts +56 -0
  144. package/templates/apps/web/src/styles.css +165 -0
  145. package/templates/apps/web/src/test-setup.ts +1 -0
  146. package/templates/apps/web/src/theme/index.ts +30 -0
  147. package/templates/apps/web/src/vite-env.d.ts +19 -0
  148. package/templates/apps/web/tsconfig.app.json +24 -0
  149. package/templates/apps/web/tsconfig.json +22 -0
  150. package/templates/apps/web/tsconfig.spec.json +28 -0
  151. package/templates/apps/web/vite.config.ts +87 -0
  152. package/templates/manifest.json +28 -0
  153. package/templates/packages/api-client/.eslintrc.json +18 -0
  154. package/templates/packages/api-client/jest.config.ts +13 -0
  155. package/templates/packages/api-client/package.json +8 -0
  156. package/templates/packages/api-client/project.json.template +34 -0
  157. package/templates/packages/api-client/src/__tests__/api-client.spec.ts +408 -0
  158. package/templates/packages/api-client/src/api-client.ts +201 -0
  159. package/templates/packages/api-client/src/config.ts +193 -0
  160. package/templates/packages/api-client/src/index.ts +9 -0
  161. package/templates/packages/api-client/tsconfig.json +22 -0
  162. package/templates/packages/api-client/tsconfig.lib.json +11 -0
  163. package/templates/packages/api-client/tsconfig.spec.json +14 -0
  164. package/templates/packages/common-types/.eslintrc.json +18 -0
  165. package/templates/packages/common-types/package.json +6 -0
  166. package/templates/packages/common-types/project.json.template +26 -0
  167. package/templates/packages/common-types/src/api.types.ts +24 -0
  168. package/templates/packages/common-types/src/auth.types.ts +36 -0
  169. package/templates/packages/common-types/src/common.types.ts +46 -0
  170. package/templates/packages/common-types/src/index.ts +19 -0
  171. package/templates/packages/common-types/src/lambda.types.ts +39 -0
  172. package/templates/packages/common-types/src/user.types.ts +31 -0
  173. package/templates/packages/common-types/tsconfig.json +19 -0
  174. package/templates/packages/common-types/tsconfig.lib.json +11 -0
  175. package/templates/root/.editorconfig +23 -0
  176. package/templates/root/.nvmrc +1 -0
  177. package/templates/root/eslint.config.js +61 -0
  178. package/templates/root/jest.preset.js +16 -0
  179. package/templates/root/nx.json +29 -0
  180. package/templates/root/package.json +131 -0
  181. package/templates/root/tsconfig.base.json +29 -0
@@ -0,0 +1,367 @@
1
+ import { StatusBar } from "expo-status-bar";
2
+ import {
3
+ StyleSheet,
4
+ Text,
5
+ View,
6
+ ScrollView,
7
+ TouchableOpacity,
8
+ ActivityIndicator,
9
+ Alert,
10
+ } from "react-native";
11
+ import { useEffect } from "react";
12
+ import { useUserStore } from "./store/user-store";
13
+ import type { User } from "{{PACKAGE_SCOPE}}/common-types";
14
+ import { apiClient } from "./config/api";
15
+ import { ApiError } from "{{PACKAGE_SCOPE}}/api-client";
16
+
17
+ export default function App() {
18
+ const {
19
+ user,
20
+ users,
21
+ setUser,
22
+ addUser,
23
+ setUsers,
24
+ isLoading,
25
+ error,
26
+ setLoading,
27
+ setError,
28
+ clearError,
29
+ } = useUserStore();
30
+
31
+ // Fetch users on mount
32
+ useEffect(() => {
33
+ handleFetchUsers();
34
+ }, []);
35
+
36
+ const handleLoadDemoUser = () => {
37
+ const demoUser: User = {
38
+ id: crypto.randomUUID(),
39
+ email: "demo@example.com",
40
+ name: "Demo User",
41
+ createdAt: new Date().toISOString(),
42
+ };
43
+ setUser(demoUser);
44
+ addUser(demoUser);
45
+ Alert.alert("User loaded", "Demo user has been loaded successfully");
46
+ };
47
+
48
+ const handleClearUser = () => {
49
+ setUser(null);
50
+ Alert.alert("User cleared", "Current user has been cleared");
51
+ };
52
+
53
+ const handleFetchUsers = async () => {
54
+ setLoading(true);
55
+ clearError();
56
+ try {
57
+ const fetchedUsers = await apiClient.getUsers();
58
+ setUsers(fetchedUsers);
59
+ Alert.alert(
60
+ "Users fetched",
61
+ `Loaded ${fetchedUsers.length} users from API`
62
+ );
63
+ } catch (err) {
64
+ const message =
65
+ err instanceof ApiError ? err.message : "Failed to fetch users";
66
+ setError(message);
67
+ Alert.alert("Error", message);
68
+ } finally {
69
+ setLoading(false);
70
+ }
71
+ };
72
+
73
+ const handleCreateUser = async () => {
74
+ setLoading(true);
75
+ clearError();
76
+ try {
77
+ const newUser = await apiClient.createUser({
78
+ email: `user${Date.now()}@example.com`,
79
+ name: `User ${Date.now()}`,
80
+ });
81
+ addUser(newUser);
82
+ Alert.alert("User created", `Created ${newUser.name}`);
83
+ } catch (err) {
84
+ const message =
85
+ err instanceof ApiError ? err.message : "Failed to create user";
86
+ setError(message);
87
+ Alert.alert("Error", message);
88
+ } finally {
89
+ setLoading(false);
90
+ }
91
+ };
92
+
93
+ return (
94
+ <View style={styles.container}>
95
+ <StatusBar style="light" />
96
+
97
+ {/* Header */}
98
+ <View style={styles.header}>
99
+ <Text style={styles.title}>{{PROJECT_NAME_TITLE}}</Text>
100
+ <Text style={styles.subtitle}>
101
+ Nx Monorepo with React, AWS Lambda, and Shared Types
102
+ </Text>
103
+ </View>
104
+
105
+ {/* Main Content */}
106
+ <ScrollView
107
+ style={styles.content}
108
+ contentContainerStyle={styles.contentContainer}
109
+ >
110
+ {/* Welcome Card */}
111
+ <View style={styles.card}>
112
+ <Text style={styles.cardTitle}>Welcome to Mobile Client</Text>
113
+ <Text style={styles.cardText}>
114
+ This is a React Native application built with Expo and TypeScript,
115
+ managed by Nx in a monorepo structure.
116
+ </Text>
117
+ </View>
118
+
119
+ {/* Features Card */}
120
+ <View style={styles.card}>
121
+ <Text style={styles.cardTitle}>Features:</Text>
122
+ <View style={styles.featureList}>
123
+ <Text style={styles.featureItem}>- React Native with Expo</Text>
124
+ <Text style={styles.featureItem}>- TypeScript Support</Text>
125
+ <Text style={styles.featureItem}>
126
+ - Cross-platform (iOS & Android)
127
+ </Text>
128
+ <Text style={styles.featureItem}>
129
+ - Zustand for state management
130
+ </Text>
131
+ <Text style={styles.featureItem}>- Jest for testing</Text>
132
+ <Text style={styles.featureItem}>
133
+ - Shared types from common-types library
134
+ </Text>
135
+ <Text style={styles.featureItem}>- Nx for monorepo management</Text>
136
+ <Text style={styles.featureItem}>
137
+ - Type-safe API client with Axios
138
+ </Text>
139
+ <Text style={styles.featureItem}>
140
+ - AWS CDK infrastructure deployment
141
+ </Text>
142
+ </View>
143
+ </View>
144
+
145
+ {/* API Actions Card */}
146
+ <View style={styles.card}>
147
+ <Text style={styles.cardTitle}>API Actions:</Text>
148
+ <View style={styles.buttonGroup}>
149
+ <TouchableOpacity
150
+ style={[styles.button, styles.buttonPrimary]}
151
+ onPress={handleFetchUsers}
152
+ disabled={isLoading}
153
+ >
154
+ {isLoading ? (
155
+ <ActivityIndicator color="#fff" />
156
+ ) : (
157
+ <Text style={styles.buttonText}>Fetch Users</Text>
158
+ )}
159
+ </TouchableOpacity>
160
+
161
+ <TouchableOpacity
162
+ style={[styles.button, styles.buttonSuccess]}
163
+ onPress={handleCreateUser}
164
+ disabled={isLoading}
165
+ >
166
+ {isLoading ? (
167
+ <ActivityIndicator color="#fff" />
168
+ ) : (
169
+ <Text style={styles.buttonText}>Create Test User</Text>
170
+ )}
171
+ </TouchableOpacity>
172
+ </View>
173
+ {error && <Text style={styles.errorText}>Error: {error}</Text>}
174
+ </View>
175
+
176
+ {/* Current User Card */}
177
+ {user ? (
178
+ <View style={styles.card}>
179
+ <Text style={styles.cardTitle}>Current User:</Text>
180
+ <View style={styles.userInfo}>
181
+ <View style={styles.userInfoRow}>
182
+ <Text style={styles.userInfoLabel}>ID:</Text>
183
+ <Text style={styles.userInfoValue}>{user.id}</Text>
184
+ </View>
185
+ <View style={styles.userInfoRow}>
186
+ <Text style={styles.userInfoLabel}>Email:</Text>
187
+ <Text style={styles.userInfoValue}>{user.email}</Text>
188
+ </View>
189
+ <View style={styles.userInfoRow}>
190
+ <Text style={styles.userInfoLabel}>Name:</Text>
191
+ <Text style={styles.userInfoValue}>{user.name}</Text>
192
+ </View>
193
+ <View style={styles.userInfoRow}>
194
+ <Text style={styles.userInfoLabel}>Created:</Text>
195
+ <Text style={styles.userInfoValue}>
196
+ {new Date(user.createdAt).toLocaleString()}
197
+ </Text>
198
+ </View>
199
+ </View>
200
+ <TouchableOpacity
201
+ style={[styles.button, styles.buttonDanger]}
202
+ onPress={handleClearUser}
203
+ >
204
+ <Text style={styles.buttonText}>Clear User</Text>
205
+ </TouchableOpacity>
206
+ </View>
207
+ ) : (
208
+ <TouchableOpacity
209
+ style={[styles.button, styles.buttonPrimary]}
210
+ onPress={handleLoadDemoUser}
211
+ >
212
+ <Text style={styles.buttonText}>Load Demo User</Text>
213
+ </TouchableOpacity>
214
+ )}
215
+
216
+ {/* Users Count */}
217
+ {users.length > 0 && (
218
+ <View style={styles.statsCard}>
219
+ <Text style={styles.statsText}>
220
+ Total users in store: {users.length}
221
+ </Text>
222
+ </View>
223
+ )}
224
+
225
+ {/* Footer Spacer */}
226
+ <View style={{ height: 20 }} />
227
+ </ScrollView>
228
+
229
+ {/* Footer */}
230
+ <View style={styles.footer}>
231
+ <Text style={styles.footerText}>
232
+ Built with React Native, TypeScript, Expo, and Zustand
233
+ </Text>
234
+ </View>
235
+ </View>
236
+ );
237
+ }
238
+
239
+ const styles = StyleSheet.create({
240
+ container: {
241
+ flex: 1,
242
+ backgroundColor: "#0f172a",
243
+ },
244
+ header: {
245
+ backgroundColor: "#3B82F6",
246
+ paddingTop: 60,
247
+ paddingBottom: 30,
248
+ paddingHorizontal: 20,
249
+ alignItems: "center",
250
+ },
251
+ title: {
252
+ fontSize: 32,
253
+ fontWeight: "bold",
254
+ color: "#fff",
255
+ marginBottom: 8,
256
+ },
257
+ subtitle: {
258
+ fontSize: 16,
259
+ color: "#e0e7ff",
260
+ },
261
+ content: {
262
+ flex: 1,
263
+ },
264
+ contentContainer: {
265
+ padding: 20,
266
+ },
267
+ card: {
268
+ backgroundColor: "#1e293b",
269
+ borderRadius: 12,
270
+ padding: 20,
271
+ marginBottom: 16,
272
+ borderWidth: 1,
273
+ borderColor: "#334155",
274
+ },
275
+ cardTitle: {
276
+ fontSize: 20,
277
+ fontWeight: "bold",
278
+ color: "#fff",
279
+ marginBottom: 12,
280
+ },
281
+ cardText: {
282
+ fontSize: 14,
283
+ color: "#94a3b8",
284
+ lineHeight: 20,
285
+ },
286
+ featureList: {
287
+ gap: 8,
288
+ },
289
+ featureItem: {
290
+ fontSize: 14,
291
+ color: "#94a3b8",
292
+ lineHeight: 20,
293
+ },
294
+ buttonGroup: {
295
+ gap: 12,
296
+ },
297
+ button: {
298
+ paddingVertical: 14,
299
+ paddingHorizontal: 20,
300
+ borderRadius: 8,
301
+ alignItems: "center",
302
+ justifyContent: "center",
303
+ minHeight: 50,
304
+ },
305
+ buttonPrimary: {
306
+ backgroundColor: "#8b5cf6",
307
+ },
308
+ buttonSuccess: {
309
+ backgroundColor: "#10b981",
310
+ },
311
+ buttonDanger: {
312
+ backgroundColor: "#ef4444",
313
+ marginTop: 12,
314
+ },
315
+ buttonText: {
316
+ color: "#fff",
317
+ fontSize: 16,
318
+ fontWeight: "600",
319
+ },
320
+ errorText: {
321
+ color: "#f87171",
322
+ fontSize: 14,
323
+ marginTop: 8,
324
+ },
325
+ userInfo: {
326
+ gap: 12,
327
+ marginBottom: 12,
328
+ },
329
+ userInfoRow: {
330
+ flexDirection: "row",
331
+ alignItems: "center",
332
+ },
333
+ userInfoLabel: {
334
+ fontSize: 14,
335
+ fontWeight: "600",
336
+ color: "#94a3b8",
337
+ width: 80,
338
+ },
339
+ userInfoValue: {
340
+ fontSize: 14,
341
+ color: "#e2e8f0",
342
+ flex: 1,
343
+ },
344
+ statsCard: {
345
+ backgroundColor: "#1e293b",
346
+ borderRadius: 8,
347
+ padding: 16,
348
+ borderWidth: 1,
349
+ borderColor: "#334155",
350
+ },
351
+ statsText: {
352
+ color: "#94a3b8",
353
+ fontSize: 14,
354
+ },
355
+ footer: {
356
+ backgroundColor: "#1e293b",
357
+ paddingVertical: 20,
358
+ paddingHorizontal: 20,
359
+ borderTopWidth: 1,
360
+ borderTopColor: "#374151",
361
+ },
362
+ footerText: {
363
+ textAlign: "center",
364
+ color: "#94a3b8",
365
+ fontSize: 12,
366
+ },
367
+ });
@@ -0,0 +1,46 @@
1
+ // Note: @testing-library/react-native uses react-test-renderer which
2
+ // is not yet compatible with React 19. These tests are skipped until
3
+ // the library is updated. See: https://github.com/callstack/react-native-testing-library/issues/1531
4
+
5
+ // Skipping tests due to React 19 incompatibility with react-test-renderer
6
+ describe.skip("App", () => {
7
+ it("should render the app title", () => {
8
+ // Test skipped - waiting for react-test-renderer React 19 support
9
+ });
10
+
11
+ it("should render the subtitle", () => {
12
+ // Test skipped
13
+ });
14
+
15
+ it("should render welcome message", () => {
16
+ // Test skipped
17
+ });
18
+
19
+ it("should render feature list", () => {
20
+ // Test skipped
21
+ });
22
+
23
+ it("should render Load Demo User button when no user", () => {
24
+ // Test skipped
25
+ });
26
+
27
+ it("should load demo user when button is pressed", () => {
28
+ // Test skipped
29
+ });
30
+
31
+ it("should clear user when Clear User button is pressed", () => {
32
+ // Test skipped
33
+ });
34
+
35
+ it("should render API action buttons", () => {
36
+ // Test skipped
37
+ });
38
+
39
+ it("should display users count when users exist", () => {
40
+ // Test skipped
41
+ });
42
+
43
+ it("should render footer", () => {
44
+ // Test skipped
45
+ });
46
+ });
@@ -0,0 +1,156 @@
1
+ import { useUserStore } from '../../store/user-store';
2
+ import type { User } from '{{PACKAGE_SCOPE}}/common-types';
3
+
4
+ describe('useUserStore', () => {
5
+ const mockUser: User = {
6
+ id: '1',
7
+ email: 'test@example.com',
8
+ name: 'Test User',
9
+ createdAt: '2024-01-01T00:00:00.000Z',
10
+ };
11
+
12
+ beforeEach(() => {
13
+ // Reset store before each test
14
+ useUserStore.setState({
15
+ user: null,
16
+ users: [],
17
+ isLoading: false,
18
+ error: null,
19
+ });
20
+ });
21
+
22
+ describe('initial state', () => {
23
+ it('should have null user', () => {
24
+ expect(useUserStore.getState().user).toBeNull();
25
+ });
26
+
27
+ it('should have empty users array', () => {
28
+ expect(useUserStore.getState().users).toEqual([]);
29
+ });
30
+
31
+ it('should not be loading', () => {
32
+ expect(useUserStore.getState().isLoading).toBe(false);
33
+ });
34
+
35
+ it('should have no error', () => {
36
+ expect(useUserStore.getState().error).toBeNull();
37
+ });
38
+ });
39
+
40
+ describe('setUser', () => {
41
+ it('should set the current user', () => {
42
+ useUserStore.getState().setUser(mockUser);
43
+ expect(useUserStore.getState().user).toEqual(mockUser);
44
+ });
45
+
46
+ it('should clear the current user when set to null', () => {
47
+ useUserStore.getState().setUser(mockUser);
48
+ useUserStore.getState().setUser(null);
49
+ expect(useUserStore.getState().user).toBeNull();
50
+ });
51
+ });
52
+
53
+ describe('setUsers', () => {
54
+ it('should set the users array', () => {
55
+ const users = [mockUser, { ...mockUser, id: '2', email: 'test2@example.com' }];
56
+ useUserStore.getState().setUsers(users);
57
+ expect(useUserStore.getState().users).toEqual(users);
58
+ });
59
+ });
60
+
61
+ describe('addUser', () => {
62
+ it('should add a user to the array', () => {
63
+ useUserStore.getState().addUser(mockUser);
64
+ expect(useUserStore.getState().users).toContainEqual(mockUser);
65
+ });
66
+
67
+ it('should append to existing users', () => {
68
+ const existingUser = { ...mockUser, id: '0' };
69
+ useUserStore.setState({ users: [existingUser] });
70
+
71
+ useUserStore.getState().addUser(mockUser);
72
+
73
+ expect(useUserStore.getState().users).toHaveLength(2);
74
+ expect(useUserStore.getState().users[0]).toEqual(existingUser);
75
+ expect(useUserStore.getState().users[1]).toEqual(mockUser);
76
+ });
77
+ });
78
+
79
+ describe('updateUser', () => {
80
+ it('should update a user in the array', () => {
81
+ useUserStore.setState({ users: [mockUser] });
82
+
83
+ useUserStore.getState().updateUser('1', { name: 'Updated Name' });
84
+
85
+ expect(useUserStore.getState().users[0].name).toBe('Updated Name');
86
+ });
87
+
88
+ it('should update the current user if it matches', () => {
89
+ useUserStore.setState({ user: mockUser, users: [mockUser] });
90
+
91
+ useUserStore.getState().updateUser('1', { name: 'Updated Name' });
92
+
93
+ expect(useUserStore.getState().user?.name).toBe('Updated Name');
94
+ });
95
+
96
+ it('should not update current user if id does not match', () => {
97
+ useUserStore.setState({ user: mockUser, users: [mockUser] });
98
+
99
+ useUserStore.getState().updateUser('different-id', { name: 'Updated Name' });
100
+
101
+ expect(useUserStore.getState().user?.name).toBe('Test User');
102
+ });
103
+ });
104
+
105
+ describe('removeUser', () => {
106
+ it('should remove a user from the array', () => {
107
+ useUserStore.setState({ users: [mockUser] });
108
+
109
+ useUserStore.getState().removeUser('1');
110
+
111
+ expect(useUserStore.getState().users).toHaveLength(0);
112
+ });
113
+
114
+ it('should clear current user if it matches removed user', () => {
115
+ useUserStore.setState({ user: mockUser, users: [mockUser] });
116
+
117
+ useUserStore.getState().removeUser('1');
118
+
119
+ expect(useUserStore.getState().user).toBeNull();
120
+ });
121
+
122
+ it('should not clear current user if id does not match', () => {
123
+ useUserStore.setState({ user: mockUser, users: [mockUser] });
124
+
125
+ useUserStore.getState().removeUser('different-id');
126
+
127
+ expect(useUserStore.getState().user).toEqual(mockUser);
128
+ });
129
+ });
130
+
131
+ describe('loading state', () => {
132
+ it('should set loading to true', () => {
133
+ useUserStore.getState().setLoading(true);
134
+ expect(useUserStore.getState().isLoading).toBe(true);
135
+ });
136
+
137
+ it('should set loading to false', () => {
138
+ useUserStore.setState({ isLoading: true });
139
+ useUserStore.getState().setLoading(false);
140
+ expect(useUserStore.getState().isLoading).toBe(false);
141
+ });
142
+ });
143
+
144
+ describe('error handling', () => {
145
+ it('should set error message', () => {
146
+ useUserStore.getState().setError('Something went wrong');
147
+ expect(useUserStore.getState().error).toBe('Something went wrong');
148
+ });
149
+
150
+ it('should clear error', () => {
151
+ useUserStore.setState({ error: 'Some error' });
152
+ useUserStore.getState().clearError();
153
+ expect(useUserStore.getState().error).toBeNull();
154
+ });
155
+ });
156
+ });
@@ -0,0 +1,16 @@
1
+ import { createApiClient, createConfigFromEnv } from '{{PACKAGE_SCOPE}}/api-client';
2
+
3
+ /**
4
+ * Configured API client instance
5
+ *
6
+ * Configuration is loaded from environment variables:
7
+ * - EXPO_PUBLIC_API_BASE_URL: API base URL (default: http://localhost:3000)
8
+ * - EXPO_PUBLIC_API_TIMEOUT: Request timeout in milliseconds
9
+ * - EXPO_PUBLIC_API_KEY: API key for authentication
10
+ * - EXPO_PUBLIC_API_WITH_CREDENTIALS: Whether to send credentials
11
+ * - EXPO_PUBLIC_API_DEBUG: Enable debug logging
12
+ *
13
+ * Create a .env file in the mobile app directory with these variables.
14
+ * See ENV_EXAMPLE.md in the api-client package for more details.
15
+ */
16
+ export const apiClient = createApiClient(createConfigFromEnv());
@@ -0,0 +1,56 @@
1
+ import { create } from 'zustand';
2
+ import type { User } from '{{PACKAGE_SCOPE}}/common-types';
3
+
4
+ interface UserState {
5
+ user: User | null;
6
+ users: User[];
7
+ isLoading: boolean;
8
+ error: string | null;
9
+ setUser: (user: User | null) => void;
10
+ setUsers: (users: User[]) => void;
11
+ addUser: (user: User) => void;
12
+ updateUser: (id: string, updates: Partial<User>) => void;
13
+ removeUser: (id: string) => void;
14
+ setLoading: (loading: boolean) => void;
15
+ setError: (error: string | null) => void;
16
+ clearError: () => void;
17
+ }
18
+
19
+ export const useUserStore = create<UserState>((set) => ({
20
+ user: null,
21
+ users: [],
22
+ isLoading: false,
23
+ error: null,
24
+
25
+ setUser: (user) => set({ user }),
26
+
27
+ setUsers: (users) => set({ users }),
28
+
29
+ addUser: (user) =>
30
+ set((state) => ({
31
+ users: [...state.users, user],
32
+ })),
33
+
34
+ updateUser: (id, updates) =>
35
+ set((state) => ({
36
+ users: state.users.map((u) =>
37
+ u.id === id ? { ...u, ...updates } : u
38
+ ),
39
+ user:
40
+ state.user?.id === id
41
+ ? { ...state.user, ...updates }
42
+ : state.user,
43
+ })),
44
+
45
+ removeUser: (id) =>
46
+ set((state) => ({
47
+ users: state.users.filter((u) => u.id !== id),
48
+ user: state.user?.id === id ? null : state.user,
49
+ })),
50
+
51
+ setLoading: (loading) => set({ isLoading: loading }),
52
+
53
+ setError: (error) => set({ error }),
54
+
55
+ clearError: () => set({ error: null }),
56
+ }));
@@ -0,0 +1,10 @@
1
+ import '@testing-library/jest-native/extend-expect';
2
+
3
+ // Mock expo-status-bar
4
+ jest.mock('expo-status-bar', () => ({
5
+ StatusBar: 'StatusBar',
6
+ }));
7
+
8
+ // Silence console warnings in tests
9
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
10
+ jest.spyOn(console, 'warn').mockImplementation(() => { });
@@ -0,0 +1,22 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "jsx": "react-native",
5
+ "allowJs": true,
6
+ "esModuleInterop": true,
7
+ "allowSyntheticDefaultImports": true,
8
+ "forceConsistentCasingInFileNames": true,
9
+ "strict": true,
10
+ "skipLibCheck": true,
11
+ "resolveJsonModule": true,
12
+ "isolatedModules": true,
13
+ "noEmit": true
14
+ },
15
+ "include": [
16
+ "**/*.ts",
17
+ "**/*.tsx"
18
+ ],
19
+ "exclude": [
20
+ "node_modules"
21
+ ]
22
+ }
@@ -0,0 +1,13 @@
1
+ # API Configuration
2
+ VITE_API_BASE_URL=https://your-api-domain.cloudfront.net
3
+
4
+ # Cognito Configuration (when using Cognito auth provider)
5
+ VITE_COGNITO_USER_POOL_ID=
6
+ VITE_COGNITO_CLIENT_ID=
7
+ # Optional: Identity Pool for AWS credentials (when social-login enabled)
8
+ # VITE_COGNITO_IDENTITY_POOL_ID=
9
+
10
+ # Auth0 Configuration (when using Auth0 auth provider)
11
+ VITE_AUTH0_DOMAIN=your-tenant.auth0.com
12
+ VITE_AUTH0_CLIENT_ID=your-client-id
13
+ VITE_AUTH0_AUDIENCE=https://your-api-identifier