create-stackr 0.2.0

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 (274) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +642 -0
  3. package/bin/cli.js +12 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +113 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/config/dependencies.d.ts +82 -0
  9. package/dist/config/dependencies.d.ts.map +1 -0
  10. package/dist/config/dependencies.js +82 -0
  11. package/dist/config/dependencies.js.map +1 -0
  12. package/dist/config/presets.d.ts +3 -0
  13. package/dist/config/presets.d.ts.map +1 -0
  14. package/dist/config/presets.js +174 -0
  15. package/dist/config/presets.js.map +1 -0
  16. package/dist/generators/index.d.ts +40 -0
  17. package/dist/generators/index.d.ts.map +1 -0
  18. package/dist/generators/index.js +130 -0
  19. package/dist/generators/index.js.map +1 -0
  20. package/dist/generators/onboarding.d.ts +8 -0
  21. package/dist/generators/onboarding.d.ts.map +1 -0
  22. package/dist/generators/onboarding.js +141 -0
  23. package/dist/generators/onboarding.js.map +1 -0
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +65 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/prompts/features.d.ts +14 -0
  29. package/dist/prompts/features.d.ts.map +1 -0
  30. package/dist/prompts/features.js +96 -0
  31. package/dist/prompts/features.js.map +1 -0
  32. package/dist/prompts/index.d.ts +3 -0
  33. package/dist/prompts/index.d.ts.map +1 -0
  34. package/dist/prompts/index.js +93 -0
  35. package/dist/prompts/index.js.map +1 -0
  36. package/dist/prompts/onboarding.d.ts +6 -0
  37. package/dist/prompts/onboarding.d.ts.map +1 -0
  38. package/dist/prompts/onboarding.js +37 -0
  39. package/dist/prompts/onboarding.js.map +1 -0
  40. package/dist/prompts/orm.d.ts +3 -0
  41. package/dist/prompts/orm.d.ts.map +1 -0
  42. package/dist/prompts/orm.js +23 -0
  43. package/dist/prompts/orm.js.map +1 -0
  44. package/dist/prompts/packageManager.d.ts +2 -0
  45. package/dist/prompts/packageManager.d.ts.map +1 -0
  46. package/dist/prompts/packageManager.js +18 -0
  47. package/dist/prompts/packageManager.js.map +1 -0
  48. package/dist/prompts/platform.d.ts +3 -0
  49. package/dist/prompts/platform.d.ts.map +1 -0
  50. package/dist/prompts/platform.js +21 -0
  51. package/dist/prompts/platform.js.map +1 -0
  52. package/dist/prompts/preset.d.ts +4 -0
  53. package/dist/prompts/preset.d.ts.map +1 -0
  54. package/dist/prompts/preset.js +165 -0
  55. package/dist/prompts/preset.js.map +1 -0
  56. package/dist/prompts/project.d.ts +2 -0
  57. package/dist/prompts/project.d.ts.map +1 -0
  58. package/dist/prompts/project.js +27 -0
  59. package/dist/prompts/project.js.map +1 -0
  60. package/dist/prompts/sdks.d.ts +2 -0
  61. package/dist/prompts/sdks.d.ts.map +1 -0
  62. package/dist/prompts/sdks.js +46 -0
  63. package/dist/prompts/sdks.js.map +1 -0
  64. package/dist/types/index.d.ts +77 -0
  65. package/dist/types/index.d.ts.map +1 -0
  66. package/dist/types/index.js +25 -0
  67. package/dist/types/index.js.map +1 -0
  68. package/dist/utils/cleanup.d.ts +5 -0
  69. package/dist/utils/cleanup.d.ts.map +1 -0
  70. package/dist/utils/cleanup.js +38 -0
  71. package/dist/utils/cleanup.js.map +1 -0
  72. package/dist/utils/copy.d.ts +10 -0
  73. package/dist/utils/copy.d.ts.map +1 -0
  74. package/dist/utils/copy.js +53 -0
  75. package/dist/utils/copy.js.map +1 -0
  76. package/dist/utils/errors.d.ts +33 -0
  77. package/dist/utils/errors.d.ts.map +1 -0
  78. package/dist/utils/errors.js +136 -0
  79. package/dist/utils/errors.js.map +1 -0
  80. package/dist/utils/git.d.ts +5 -0
  81. package/dist/utils/git.d.ts.map +1 -0
  82. package/dist/utils/git.js +33 -0
  83. package/dist/utils/git.js.map +1 -0
  84. package/dist/utils/logger.d.ts +9 -0
  85. package/dist/utils/logger.d.ts.map +1 -0
  86. package/dist/utils/logger.js +22 -0
  87. package/dist/utils/logger.js.map +1 -0
  88. package/dist/utils/package.d.ts +16 -0
  89. package/dist/utils/package.d.ts.map +1 -0
  90. package/dist/utils/package.js +86 -0
  91. package/dist/utils/package.js.map +1 -0
  92. package/dist/utils/system-validation.d.ts +9 -0
  93. package/dist/utils/system-validation.d.ts.map +1 -0
  94. package/dist/utils/system-validation.js +31 -0
  95. package/dist/utils/system-validation.js.map +1 -0
  96. package/dist/utils/template.d.ts +20 -0
  97. package/dist/utils/template.d.ts.map +1 -0
  98. package/dist/utils/template.js +234 -0
  99. package/dist/utils/template.js.map +1 -0
  100. package/dist/utils/validation.d.ts +8 -0
  101. package/dist/utils/validation.d.ts.map +1 -0
  102. package/dist/utils/validation.js +94 -0
  103. package/dist/utils/validation.js.map +1 -0
  104. package/package.json +96 -0
  105. package/templates/base/backend/.dockerignore.ejs +62 -0
  106. package/templates/base/backend/.env.example.ejs +116 -0
  107. package/templates/base/backend/Dockerfile.ejs +142 -0
  108. package/templates/base/backend/controllers/event-queue/index.ts +20 -0
  109. package/templates/base/backend/controllers/event-queue/workers/user.ts +39 -0
  110. package/templates/base/backend/controllers/rest-api/index.ts +48 -0
  111. package/templates/base/backend/controllers/rest-api/plugins/auth.ts +152 -0
  112. package/templates/base/backend/controllers/rest-api/plugins/config.ts +64 -0
  113. package/templates/base/backend/controllers/rest-api/plugins/error-handler.ts +118 -0
  114. package/templates/base/backend/controllers/rest-api/routes/auth.ts.ejs +180 -0
  115. package/templates/base/backend/controllers/rest-api/routes/device-sessions.ts +197 -0
  116. package/templates/base/backend/controllers/rest-api/routes/oauth-web.ts.ejs +375 -0
  117. package/templates/base/backend/controllers/rest-api/server.ts.ejs +87 -0
  118. package/templates/base/backend/domain/device-session/repository.drizzle.ts +209 -0
  119. package/templates/base/backend/domain/device-session/repository.prisma.ts +248 -0
  120. package/templates/base/backend/domain/device-session/schema.ts +72 -0
  121. package/templates/base/backend/domain/session/repository.drizzle.ts +72 -0
  122. package/templates/base/backend/domain/session/repository.prisma.ts +72 -0
  123. package/templates/base/backend/domain/session/schema.ts +29 -0
  124. package/templates/base/backend/domain/user/repository.drizzle.ts +127 -0
  125. package/templates/base/backend/domain/user/repository.prisma.ts +115 -0
  126. package/templates/base/backend/domain/user/schema.ts +14 -0
  127. package/templates/base/backend/drizzle/schema.drizzle.ts +111 -0
  128. package/templates/base/backend/drizzle.config.drizzle.ts +13 -0
  129. package/templates/base/backend/lib/auth.drizzle.ts.ejs +104 -0
  130. package/templates/base/backend/lib/auth.prisma.ts.ejs +97 -0
  131. package/templates/base/backend/lib/constants.ts.ejs +29 -0
  132. package/templates/base/backend/package.json.ejs +50 -0
  133. package/templates/base/backend/prisma/schema.prisma.ejs +102 -0
  134. package/templates/base/backend/prisma.config.prisma.ts +12 -0
  135. package/templates/base/backend/tsconfig.json +39 -0
  136. package/templates/base/backend/utils/db.drizzle.ts +41 -0
  137. package/templates/base/backend/utils/db.prisma.ts +51 -0
  138. package/templates/base/backend/utils/email.ts.ejs +35 -0
  139. package/templates/base/backend/utils/errors.ts +348 -0
  140. package/templates/base/backend/utils/redis.ts.ejs +279 -0
  141. package/templates/base/mobile/.env.example.ejs +35 -0
  142. package/templates/base/mobile/.gitignore.ejs +167 -0
  143. package/templates/base/mobile/app/+not-found.tsx +85 -0
  144. package/templates/base/mobile/app/_layout.tsx.ejs +71 -0
  145. package/templates/base/mobile/app.json.ejs +88 -0
  146. package/templates/base/mobile/assets/images/adaptive-icon.png +0 -0
  147. package/templates/base/mobile/assets/images/favicon.png +0 -0
  148. package/templates/base/mobile/assets/images/icon.png +0 -0
  149. package/templates/base/mobile/assets/images/onboarding_page_1.png +0 -0
  150. package/templates/base/mobile/assets/images/onboarding_page_2.png +0 -0
  151. package/templates/base/mobile/assets/images/onboarding_page_3.png +0 -0
  152. package/templates/base/mobile/assets/images/paywall_image.png +0 -0
  153. package/templates/base/mobile/assets/images/splash.png +0 -0
  154. package/templates/base/mobile/eas.json.ejs +49 -0
  155. package/templates/base/mobile/metro.config.js +9 -0
  156. package/templates/base/mobile/package.json.ejs +53 -0
  157. package/templates/base/mobile/src/components/ui/Button.tsx +131 -0
  158. package/templates/base/mobile/src/components/ui/Card.tsx +68 -0
  159. package/templates/base/mobile/src/components/ui/IconSymbol.tsx +90 -0
  160. package/templates/base/mobile/src/components/ui/Input.tsx +142 -0
  161. package/templates/base/mobile/src/components/ui/LoadingSpinner.tsx +98 -0
  162. package/templates/base/mobile/src/components/ui/OnboardingLayout.tsx +356 -0
  163. package/templates/base/mobile/src/components/ui/PaywallLayout.tsx +311 -0
  164. package/templates/base/mobile/src/components/ui/Skeleton.tsx +58 -0
  165. package/templates/base/mobile/src/components/ui/index.ts +6 -0
  166. package/templates/base/mobile/src/constants/Theme.ts +163 -0
  167. package/templates/base/mobile/src/context/ThemeContext.tsx +157 -0
  168. package/templates/base/mobile/src/lib/auth-client.ts.ejs +51 -0
  169. package/templates/base/mobile/src/services/api.ts.ejs +71 -0
  170. package/templates/base/mobile/src/services/errorService.ts +179 -0
  171. package/templates/base/mobile/src/services/sdkInitializer.ts.ejs +36 -0
  172. package/templates/base/mobile/src/store/index.ts.ejs +18 -0
  173. package/templates/base/mobile/src/store/ui.store.ts +100 -0
  174. package/templates/base/mobile/src/utils/formatters.ts +105 -0
  175. package/templates/base/mobile/src/utils/logger.ts +73 -0
  176. package/templates/base/mobile/src/utils/responsive.ts +234 -0
  177. package/templates/base/mobile/tsconfig.json +32 -0
  178. package/templates/base/web/.env.example.ejs +26 -0
  179. package/templates/base/web/components.json +22 -0
  180. package/templates/base/web/eslint.config.mjs +18 -0
  181. package/templates/base/web/next.config.ts +7 -0
  182. package/templates/base/web/package.json.ejs +35 -0
  183. package/templates/base/web/postcss.config.mjs +7 -0
  184. package/templates/base/web/public/.gitkeep +0 -0
  185. package/templates/base/web/public/file.svg +1 -0
  186. package/templates/base/web/public/globe.svg +1 -0
  187. package/templates/base/web/public/next.svg +1 -0
  188. package/templates/base/web/public/vercel.svg +1 -0
  189. package/templates/base/web/public/window.svg +1 -0
  190. package/templates/base/web/src/app/favicon.ico +0 -0
  191. package/templates/base/web/src/app/globals.css +152 -0
  192. package/templates/base/web/src/app/layout.tsx.ejs +54 -0
  193. package/templates/base/web/src/app/page.tsx.ejs +92 -0
  194. package/templates/base/web/src/components/auth/auth-hydrator.tsx.ejs +19 -0
  195. package/templates/base/web/src/components/auth/protected-route.tsx.ejs +109 -0
  196. package/templates/base/web/src/components/providers/device-session-setup.tsx.ejs +56 -0
  197. package/templates/base/web/src/components/providers/theme-provider.tsx +17 -0
  198. package/templates/base/web/src/components/theme-toggle.tsx +34 -0
  199. package/templates/base/web/src/components/ui/button.tsx +62 -0
  200. package/templates/base/web/src/components/ui/card.tsx +92 -0
  201. package/templates/base/web/src/components/ui/input.tsx +21 -0
  202. package/templates/base/web/src/components/ui/label.tsx +24 -0
  203. package/templates/base/web/src/components/ui/skeleton.tsx +13 -0
  204. package/templates/base/web/src/components/ui/spinner.tsx +20 -0
  205. package/templates/base/web/src/hooks/use-device-session.ts.ejs +40 -0
  206. package/templates/base/web/src/hooks/use-session.ts.ejs +56 -0
  207. package/templates/base/web/src/lib/auth/actions.ts.ejs +334 -0
  208. package/templates/base/web/src/lib/auth/config.ts.ejs +65 -0
  209. package/templates/base/web/src/lib/auth/cookies.ts.ejs +74 -0
  210. package/templates/base/web/src/lib/auth/index.ts.ejs +40 -0
  211. package/templates/base/web/src/lib/auth/oauth.ts.ejs +72 -0
  212. package/templates/base/web/src/lib/auth/pkce.ts.ejs +48 -0
  213. package/templates/base/web/src/lib/auth/sessions.ts.ejs +135 -0
  214. package/templates/base/web/src/lib/auth/user-agent.ts.ejs +47 -0
  215. package/templates/base/web/src/lib/device/actions.ts.ejs +148 -0
  216. package/templates/base/web/src/lib/device/id.ts.ejs +74 -0
  217. package/templates/base/web/src/lib/utils.ts +6 -0
  218. package/templates/base/web/src/proxy.ts.ejs +66 -0
  219. package/templates/base/web/src/store/auth.store.ts.ejs +89 -0
  220. package/templates/base/web/src/store/deviceSession.store.ts.ejs +141 -0
  221. package/templates/base/web/tsconfig.json +34 -0
  222. package/templates/features/mobile/auth/app/(auth)/_layout.tsx +16 -0
  223. package/templates/features/mobile/auth/app/(auth)/login.tsx +86 -0
  224. package/templates/features/mobile/auth/app/(auth)/register.tsx +86 -0
  225. package/templates/features/mobile/auth/components/auth/LoginForm.tsx.ejs +349 -0
  226. package/templates/features/mobile/auth/components/auth/RegisterForm.tsx.ejs +407 -0
  227. package/templates/features/mobile/auth/components/auth/index.ts +2 -0
  228. package/templates/features/mobile/auth/hooks/index.ts.ejs +1 -0
  229. package/templates/features/mobile/auth/hooks/useAuth.ts.ejs +367 -0
  230. package/templates/features/mobile/auth/services/deviceSession.ts +370 -0
  231. package/templates/features/mobile/auth/store/deviceSession.store.ts +326 -0
  232. package/templates/features/mobile/onboarding/app/(onboarding)/_layout.tsx.ejs +11 -0
  233. package/templates/features/mobile/onboarding/app/(onboarding)/page-1.tsx.ejs +52 -0
  234. package/templates/features/mobile/onboarding/app/(onboarding)/page-2.tsx.ejs +52 -0
  235. package/templates/features/mobile/onboarding/app/(onboarding)/page-3.tsx.ejs +60 -0
  236. package/templates/features/mobile/paywall/app/paywall.tsx +550 -0
  237. package/templates/features/mobile/tabs/app/(tabs)/_layout.tsx +26 -0
  238. package/templates/features/mobile/tabs/app/(tabs)/index.tsx +565 -0
  239. package/templates/features/web/.gitkeep +0 -0
  240. package/templates/features/web/auth/app/(app)/dashboard/dashboard-client.tsx.ejs +166 -0
  241. package/templates/features/web/auth/app/(app)/dashboard/page.tsx.ejs +24 -0
  242. package/templates/features/web/auth/app/(app)/layout.tsx.ejs +43 -0
  243. package/templates/features/web/auth/app/(app)/settings/sessions/page.tsx.ejs +29 -0
  244. package/templates/features/web/auth/app/(app)/settings/sessions/sessions-client.tsx.ejs +77 -0
  245. package/templates/features/web/auth/app/(auth)/forgot-password/page.tsx.ejs +127 -0
  246. package/templates/features/web/auth/app/(auth)/layout.tsx.ejs +32 -0
  247. package/templates/features/web/auth/app/(auth)/login/page.tsx.ejs +35 -0
  248. package/templates/features/web/auth/app/(auth)/register/page.tsx.ejs +19 -0
  249. package/templates/features/web/auth/app/(auth)/reset-password/page.tsx.ejs +40 -0
  250. package/templates/features/web/auth/app/(auth)/verify-email/page.tsx.ejs +198 -0
  251. package/templates/features/web/auth/app/auth/callback/route.ts.ejs +152 -0
  252. package/templates/features/web/auth/components/auth/login-form.tsx.ejs +100 -0
  253. package/templates/features/web/auth/components/auth/oauth-buttons.tsx.ejs +126 -0
  254. package/templates/features/web/auth/components/auth/password-reset-form.tsx.ejs +103 -0
  255. package/templates/features/web/auth/components/auth/register-form.tsx.ejs +139 -0
  256. package/templates/features/web/auth/components/settings/session-card.tsx.ejs +132 -0
  257. package/templates/integrations/mobile/adjust/services/adjustService.ts.ejs +163 -0
  258. package/templates/integrations/mobile/adjust/store/adjust.store.ts +243 -0
  259. package/templates/integrations/mobile/att/services/attService.ts +84 -0
  260. package/templates/integrations/mobile/att/services/trackingPermissions.ts +208 -0
  261. package/templates/integrations/mobile/att/store/att.store.ts +162 -0
  262. package/templates/integrations/mobile/revenuecat/services/revenuecatService.ts.ejs +174 -0
  263. package/templates/integrations/mobile/revenuecat/store/revenuecat.store.ts +286 -0
  264. package/templates/integrations/mobile/scate/services/scateService.ts.ejs +85 -0
  265. package/templates/integrations/mobile/scate/store/scate.store.ts +125 -0
  266. package/templates/integrations/web/.gitkeep +0 -0
  267. package/templates/shared/.env.example.ejs +21 -0
  268. package/templates/shared/.gitignore.ejs +145 -0
  269. package/templates/shared/README.md.ejs +134 -0
  270. package/templates/shared/docker-compose.prod.yml.ejs +120 -0
  271. package/templates/shared/docker-compose.yml.ejs +129 -0
  272. package/templates/shared/scripts/docker-dev.sh.ejs +395 -0
  273. package/templates/shared/scripts/docker-prod.sh.ejs +542 -0
  274. package/templates/shared/scripts/setup.sh.ejs +979 -0
@@ -0,0 +1,286 @@
1
+ import { create } from 'zustand';
2
+ import { revenueCatService } from '../services/revenuecatService';
3
+ import { CustomerInfo, PurchasesPackage, PurchasesOfferings } from 'react-native-purchases';
4
+ import { logger } from '../utils/logger';
5
+
6
+ interface RevenueCatState {
7
+ // State
8
+ isInitialized: boolean;
9
+ customerInfo: CustomerInfo | null;
10
+ offerings: PurchasesOfferings | null;
11
+ adjustId: string | null;
12
+ isLoading: boolean;
13
+ error: string | null;
14
+
15
+ // Actions
16
+ initialize: () => void;
17
+ setAdjustId: (adjustId: string) => void;
18
+ getCustomerInfo: () => Promise<void>;
19
+ getOfferings: () => Promise<void>;
20
+ purchasePackage: (pkg: PurchasesPackage) => Promise<{ success: boolean; userCancelled: boolean }>;
21
+ restorePurchases: () => Promise<void>;
22
+ setLoading: (loading: boolean) => void;
23
+ setError: (error: string | null) => void;
24
+ reset: () => void;
25
+
26
+ // Computed values
27
+ hasActiveSubscription: boolean;
28
+ getActiveEntitlements: () => string[];
29
+ }
30
+
31
+ export const useRevenueCatStore = create<RevenueCatState>((set, get) => ({
32
+ // Initial state
33
+ isInitialized: false,
34
+ customerInfo: null,
35
+ offerings: null,
36
+ adjustId: null,
37
+ isLoading: false,
38
+ error: null,
39
+
40
+ // Actions
41
+ initialize: () => {
42
+ const state = get();
43
+ if (state.isInitialized) {
44
+ logger.info('RevenueCatStore: Already initialized, skipping');
45
+ return;
46
+ }
47
+
48
+ try {
49
+ set({ isLoading: true, error: null });
50
+ logger.info('RevenueCatStore: Initializing RevenueCat SDK...');
51
+
52
+ revenueCatService.initialize();
53
+
54
+ set({
55
+ isInitialized: true,
56
+ isLoading: false,
57
+ error: null
58
+ });
59
+
60
+ logger.info('RevenueCatStore: RevenueCat SDK initialized successfully');
61
+
62
+ // Get initial customer info
63
+ get().getCustomerInfo();
64
+ } catch (error) {
65
+ const errorMessage = error instanceof Error ? error.message : 'Failed to initialize RevenueCat SDK';
66
+ logger.error('RevenueCatStore: Failed to initialize RevenueCat SDK', { error });
67
+ set({
68
+ error: errorMessage,
69
+ isLoading: false,
70
+ isInitialized: false
71
+ });
72
+ }
73
+ },
74
+
75
+ setAdjustId: (adjustId) => {
76
+ try {
77
+ const currentState = get();
78
+ if (currentState.adjustId === adjustId) {
79
+ logger.info('RevenueCatStore: Adjust ID already set, skipping');
80
+ return;
81
+ }
82
+
83
+ logger.info('RevenueCatStore: Setting Adjust ID', { adjustId });
84
+ revenueCatService.setAdjustId(adjustId);
85
+
86
+ set({ adjustId });
87
+ logger.info('RevenueCatStore: Adjust ID set successfully');
88
+ } catch (error) {
89
+ const errorMessage = error instanceof Error ? error.message : 'Failed to set Adjust ID';
90
+ logger.error('RevenueCatStore: Failed to set Adjust ID', { error });
91
+ set({ error: errorMessage });
92
+ }
93
+ },
94
+
95
+ getCustomerInfo: async () => {
96
+ const state = get();
97
+ if (state.isLoading) {
98
+ logger.info('RevenueCatStore: Customer info request already in progress');
99
+ return;
100
+ }
101
+
102
+ try {
103
+ set({ isLoading: true, error: null });
104
+ logger.info('RevenueCatStore: Getting customer info...');
105
+
106
+ const customerInfo = await revenueCatService.getCustomerInfo();
107
+
108
+ set({
109
+ customerInfo,
110
+ isLoading: false,
111
+ error: null
112
+ });
113
+
114
+ logger.info('RevenueCatStore: Customer info retrieved successfully');
115
+ } catch (error) {
116
+ const errorMessage = error instanceof Error ? error.message : 'Failed to get customer info';
117
+ logger.error('RevenueCatStore: Failed to get customer info', { error });
118
+ set({
119
+ error: errorMessage,
120
+ isLoading: false,
121
+ customerInfo: null
122
+ });
123
+ }
124
+ },
125
+
126
+ getOfferings: async () => {
127
+ const state = get();
128
+ if (state.isLoading) {
129
+ logger.info('RevenueCatStore: Offerings request already in progress');
130
+ return;
131
+ }
132
+
133
+ try {
134
+ set({ isLoading: true, error: null });
135
+ logger.info('RevenueCatStore: Getting offerings...');
136
+
137
+ const offerings = await revenueCatService.getOfferings();
138
+
139
+ set({
140
+ offerings,
141
+ isLoading: false,
142
+ error: null
143
+ });
144
+
145
+ logger.info('RevenueCatStore: Offerings retrieved successfully', {
146
+ offeringsCount: Object.keys(offerings.all).length,
147
+ hasCurrent: !!offerings.current,
148
+ });
149
+ } catch (error) {
150
+ const errorMessage = error instanceof Error ? error.message : 'Failed to get offerings';
151
+ logger.error('RevenueCatStore: Failed to get offerings', { error });
152
+ set({
153
+ error: errorMessage,
154
+ isLoading: false,
155
+ offerings: null
156
+ });
157
+ }
158
+ },
159
+
160
+ purchasePackage: async (pkg) => {
161
+ const state = get();
162
+ if (state.isLoading) {
163
+ logger.info('RevenueCatStore: Purchase already in progress');
164
+ return { success: false, userCancelled: false };
165
+ }
166
+
167
+ try {
168
+ set({ isLoading: true, error: null });
169
+ logger.info('RevenueCatStore: Purchasing package', { packageId: pkg.identifier });
170
+
171
+ const result = await revenueCatService.purchasePackage(pkg);
172
+
173
+ set({
174
+ customerInfo: result.customerInfo,
175
+ isLoading: false,
176
+ error: null
177
+ });
178
+
179
+ const success = !result.userCancelled;
180
+ logger.info('RevenueCatStore: Package purchase completed', {
181
+ success,
182
+ userCancelled: result.userCancelled
183
+ });
184
+
185
+ return { success, userCancelled: result.userCancelled };
186
+ } catch (error) {
187
+ const errorMessage = error instanceof Error ? error.message : 'Failed to purchase package';
188
+ logger.error('RevenueCatStore: Failed to purchase package', { error });
189
+ set({
190
+ error: errorMessage,
191
+ isLoading: false
192
+ });
193
+ return { success: false, userCancelled: false };
194
+ }
195
+ },
196
+
197
+ restorePurchases: async () => {
198
+ const state = get();
199
+ if (state.isLoading) {
200
+ logger.info('RevenueCatStore: Restore purchases already in progress');
201
+ return;
202
+ }
203
+
204
+ try {
205
+ set({ isLoading: true, error: null });
206
+ logger.info('RevenueCatStore: Restoring purchases...');
207
+
208
+ const customerInfo = await revenueCatService.restorePurchases();
209
+
210
+ set({
211
+ customerInfo,
212
+ isLoading: false,
213
+ error: null
214
+ });
215
+
216
+ logger.info('RevenueCatStore: Purchases restored successfully');
217
+ } catch (error) {
218
+ const errorMessage = error instanceof Error ? error.message : 'Failed to restore purchases';
219
+ logger.error('RevenueCatStore: Failed to restore purchases', { error });
220
+ set({
221
+ error: errorMessage,
222
+ isLoading: false
223
+ });
224
+ }
225
+ },
226
+
227
+ setLoading: (isLoading) => set({ isLoading }),
228
+
229
+ setError: (error) => set({ error }),
230
+
231
+ reset: () => {
232
+ set({
233
+ isInitialized: false,
234
+ customerInfo: null,
235
+ offerings: null,
236
+ adjustId: null,
237
+ isLoading: false,
238
+ error: null
239
+ });
240
+ revenueCatService.reset();
241
+ logger.info('RevenueCatStore: Store reset');
242
+ },
243
+
244
+ // Computed values
245
+ get hasActiveSubscription() {
246
+ const state = get();
247
+ if (!state.customerInfo) return false;
248
+ return Object.keys(state.customerInfo.entitlements.active).length > 0;
249
+ },
250
+
251
+ getActiveEntitlements: () => {
252
+ const state = get();
253
+ if (!state.customerInfo) return [];
254
+ return Object.keys(state.customerInfo.entitlements.active);
255
+ },
256
+ }));
257
+
258
+ // Selectors for commonly used RevenueCat state
259
+ export const useRevenueCat = () => {
260
+ const state = useRevenueCatStore();
261
+ return {
262
+ isInitialized: state.isInitialized,
263
+ customerInfo: state.customerInfo,
264
+ offerings: state.offerings,
265
+ adjustId: state.adjustId,
266
+ isLoading: state.isLoading,
267
+ error: state.error,
268
+ hasActiveSubscription: state.hasActiveSubscription,
269
+ activeEntitlements: state.getActiveEntitlements(),
270
+ };
271
+ };
272
+
273
+ export const useRevenueCatActions = () => {
274
+ const state = useRevenueCatStore();
275
+ return {
276
+ initialize: state.initialize,
277
+ setAdjustId: state.setAdjustId,
278
+ getCustomerInfo: state.getCustomerInfo,
279
+ getOfferings: state.getOfferings,
280
+ purchasePackage: state.purchasePackage,
281
+ restorePurchases: state.restorePurchases,
282
+ setLoading: state.setLoading,
283
+ setError: state.setError,
284
+ reset: state.reset,
285
+ };
286
+ };
@@ -0,0 +1,85 @@
1
+ import { ScateSDK } from 'scatesdk-react';
2
+ import Constants from 'expo-constants';
3
+ import { logger } from '../utils/logger';
4
+
5
+ class ScateService {
6
+ private static readonly API_KEY = Constants.expoConfig?.extra?.scate?.apiKey || '';
7
+
8
+ private _isInitialized: boolean = false;
9
+
10
+ /**
11
+ * Check if Scate SDK is initialized
12
+ */
13
+ get isInitialized(): boolean {
14
+ return this._isInitialized;
15
+ }
16
+
17
+ /**
18
+ * Initialize Scate SDK
19
+ */
20
+ initialize(): void {
21
+ if (this._isInitialized) {
22
+ logger.info('ScateService: Already initialized, skipping');
23
+ return;
24
+ }
25
+
26
+ try {
27
+ logger.info('ScateService: Initializing Scate SDK...');
28
+
29
+ ScateSDK.Init(ScateService.API_KEY);
30
+
31
+ this._isInitialized = true;
32
+ logger.info('ScateService: Scate SDK initialized successfully');
33
+ } catch (error) {
34
+ logger.error('ScateService: Failed to initialize Scate SDK', { error });
35
+ throw error;
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Set Adjust ADID for attribution
41
+ */
42
+ setAdid(adid: string): void {
43
+ try {
44
+ logger.info('ScateService: Setting ADID', { adid });
45
+ ScateSDK.SetAdid(adid);
46
+ logger.info('ScateService: ADID set successfully');
47
+ } catch (error) {
48
+ logger.error('ScateService: Failed to set ADID', { error });
49
+ throw error;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Track custom event (if needed for future features)
55
+ */
56
+ trackEvent(eventName: string, parameters?: Record<string, any>): void {
57
+ try {
58
+ logger.info('ScateService: Custom event tracking would be implemented here', {
59
+ eventName,
60
+ parameters
61
+ });
62
+ // Implementation would depend on Scate SDK's event tracking capabilities
63
+ } catch (error) {
64
+ logger.error('ScateService: Failed to track event', { error });
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Get API key
70
+ */
71
+ getApiKey(): string {
72
+ return ScateService.API_KEY;
73
+ }
74
+
75
+ /**
76
+ * Reset Scate SDK state (useful for testing)
77
+ */
78
+ reset(): void {
79
+ this._isInitialized = false;
80
+ logger.info('ScateService: Service reset');
81
+ }
82
+ }
83
+
84
+ // Export singleton instance
85
+ export const scateService = new ScateService();
@@ -0,0 +1,125 @@
1
+ import { create } from 'zustand';
2
+ import { scateService } from '../services/scateService';
3
+ import { logger } from '../utils/logger';
4
+
5
+ interface ScateState {
6
+ // State
7
+ isInitialized: boolean;
8
+ adid: string | null;
9
+ isLoading: boolean;
10
+ error: string | null;
11
+
12
+ // Actions
13
+ initialize: () => void;
14
+ setAdid: (adid: string) => void;
15
+ setLoading: (loading: boolean) => void;
16
+ setError: (error: string | null) => void;
17
+ reset: () => void;
18
+
19
+ // Computed values
20
+ hasAdid: boolean;
21
+ }
22
+
23
+ export const useScateStore = create<ScateState>((set, get) => ({
24
+ // Initial state
25
+ isInitialized: false,
26
+ adid: null,
27
+ isLoading: false,
28
+ error: null,
29
+
30
+ // Actions
31
+ initialize: () => {
32
+ const state = get();
33
+ if (state.isInitialized) {
34
+ logger.info('ScateStore: Already initialized, skipping');
35
+ return;
36
+ }
37
+
38
+ try {
39
+ set({ isLoading: true, error: null });
40
+ logger.info('ScateStore: Initializing Scate SDK...');
41
+
42
+ scateService.initialize();
43
+
44
+ set({
45
+ isInitialized: true,
46
+ isLoading: false,
47
+ error: null
48
+ });
49
+
50
+ logger.info('ScateStore: Scate SDK initialized successfully');
51
+ } catch (error) {
52
+ const errorMessage = error instanceof Error ? error.message : 'Failed to initialize Scate SDK';
53
+ logger.error('ScateStore: Failed to initialize Scate SDK', { error });
54
+ set({
55
+ error: errorMessage,
56
+ isLoading: false,
57
+ isInitialized: false
58
+ });
59
+ }
60
+ },
61
+
62
+ setAdid: (adid) => {
63
+ try {
64
+ const currentState = get();
65
+ if (currentState.adid === adid) {
66
+ logger.info('ScateStore: ADID already set, skipping');
67
+ return;
68
+ }
69
+
70
+ logger.info('ScateStore: Setting ADID', { adid });
71
+ scateService.setAdid(adid);
72
+
73
+ set({ adid });
74
+ logger.info('ScateStore: ADID set successfully');
75
+ } catch (error) {
76
+ const errorMessage = error instanceof Error ? error.message : 'Failed to set ADID';
77
+ logger.error('ScateStore: Failed to set ADID', { error });
78
+ set({ error: errorMessage });
79
+ }
80
+ },
81
+
82
+ setLoading: (isLoading) => set({ isLoading }),
83
+
84
+ setError: (error) => set({ error }),
85
+
86
+ reset: () => {
87
+ set({
88
+ isInitialized: false,
89
+ adid: null,
90
+ isLoading: false,
91
+ error: null
92
+ });
93
+ scateService.reset();
94
+ logger.info('ScateStore: Store reset');
95
+ },
96
+
97
+ // Computed values
98
+ get hasAdid() {
99
+ const state = get();
100
+ return !!state.adid;
101
+ },
102
+ }));
103
+
104
+ // Selectors for commonly used Scate state
105
+ export const useScate = () => {
106
+ const state = useScateStore();
107
+ return {
108
+ isInitialized: state.isInitialized,
109
+ adid: state.adid,
110
+ isLoading: state.isLoading,
111
+ error: state.error,
112
+ hasAdid: state.hasAdid,
113
+ };
114
+ };
115
+
116
+ export const useScateActions = () => {
117
+ const state = useScateStore();
118
+ return {
119
+ initialize: state.initialize,
120
+ setAdid: state.setAdid,
121
+ setLoading: state.setLoading,
122
+ setError: state.setError,
123
+ reset: state.reset,
124
+ };
125
+ };
File without changes
@@ -0,0 +1,21 @@
1
+ # =============================================================================
2
+ # Docker Compose Environment Variables
3
+ # =============================================================================
4
+ # These variables are used by docker-compose.yml for container orchestration.
5
+ # The setup.sh script will create .env from this template automatically.
6
+ #
7
+ # For application-specific configuration (OAuth, BetterAuth, etc.),
8
+ # see backend/.env.example
9
+ # =============================================================================
10
+
11
+ # Database Configuration (for PostgreSQL container)
12
+ DB_USER=postgres
13
+ DB_PASSWORD=your-secure-database-password
14
+ DB_NAME=<%= projectName.toLowerCase().replace(/[^a-z0-9]/g, '_') %>_db
15
+
16
+ # JWT Secret (must match between services)
17
+ JWT_SECRET=your-jwt-secret-change-in-production
18
+ <% if (backend.eventQueue) { %>
19
+ # Redis Configuration
20
+ REDIS_PASSWORD=your-redis-password
21
+ <% } %>
@@ -0,0 +1,145 @@
1
+ # Dependencies
2
+ node_modules/
3
+ .bun/
4
+
5
+ # Environment variables
6
+ .env
7
+ .env.local
8
+ .env.development.local
9
+ .env.test.local
10
+ .env.production.local
11
+ .env.staging
12
+
13
+ # Build outputs
14
+ dist/
15
+ build/
16
+ out/
17
+ .next/
18
+
19
+ # Logs
20
+ *.log
21
+ npm-debug.log*
22
+ yarn-debug.log*
23
+ yarn-error.log*
24
+ lerna-debug.log*
25
+ .pnpm-debug.log*
26
+
27
+ # Runtime data
28
+ pids/
29
+ *.pid
30
+ *.seed
31
+ *.pid.lock
32
+
33
+ # Coverage directory used by tools like istanbul
34
+ coverage/
35
+ *.lcov
36
+ .nyc_output/
37
+
38
+ # Dependency directories
39
+ jspm_packages/
40
+
41
+ # TypeScript cache
42
+ *.tsbuildinfo
43
+
44
+ # Optional npm cache directory
45
+ .npm
46
+
47
+ # Optional eslint cache
48
+ .eslintcache
49
+
50
+ # Microbundle cache
51
+ .rpt2_cache/
52
+ .rts2_cache_cjs/
53
+ .rts2_cache_es/
54
+ .rts2_cache_umd/
55
+
56
+ # Optional REPL history
57
+ .node_repl_history
58
+
59
+ # Output of 'npm pack'
60
+ *.tgz
61
+
62
+ # Yarn Integrity file
63
+ .yarn-integrity
64
+
65
+ # parcel-bundler cache (https://parceljs.org/)
66
+ .cache
67
+ .parcel-cache
68
+
69
+ # Stores VSCode versions used for testing VSCode extensions
70
+ .vscode-test
71
+
72
+ # yarn v2
73
+ .yarn/cache
74
+ .yarn/unplugged
75
+ .yarn/build-state.yml
76
+ .yarn/install-state.gz
77
+ .pnp.*
78
+
79
+ # IDEs and editors
80
+ .vscode/
81
+ .idea/
82
+ *.swp
83
+ *.swo
84
+ *~
85
+ *.sublime-*
86
+
87
+ # OS generated files
88
+ .DS_Store
89
+ .DS_Store?
90
+ ._*
91
+ .Spotlight-V100
92
+ .Trashes
93
+ ehthumbs.db
94
+ Thumbs.db
95
+
96
+ # Docker
97
+ docker-compose.override.yml
98
+ .dockerignore.local
99
+
100
+ # Temporary files
101
+ tmp/
102
+ temp/
103
+ *.tmp
104
+ *.temp
105
+
106
+ # Backup files
107
+ *.backup
108
+ *.bak
109
+ *.orig
110
+
111
+ # Database
112
+ *.sqlite
113
+ *.db
114
+ database.sqlite
115
+
116
+ # Redis dump
117
+ dump.rdb
118
+
119
+ # Prisma
120
+ prisma/migrations/migration_lock.toml
121
+
122
+ # Production deployment artifacts
123
+ .vercel/
124
+ .netlify/
125
+ .firebase/
126
+
127
+ # Local development overrides
128
+ *.local
129
+ local-*
130
+
131
+ # Test artifacts
132
+ .nyc_output/
133
+ test-results/
134
+ playwright-report/
135
+
136
+ # Certificates
137
+ *.pem
138
+ *.key
139
+ *.crt
140
+ *.cert
141
+
142
+ # Secrets
143
+ secrets/
144
+ .secrets/
145
+ *.secret