kasy-cli 1.21.8 → 1.22.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 (269) hide show
  1. package/lib/commands/add.js +93 -80
  2. package/lib/commands/configure.js +100 -32
  3. package/lib/commands/doctor.js +28 -2
  4. package/lib/commands/new.js +86 -38
  5. package/lib/commands/notifications.js +1 -1
  6. package/lib/commands/remove.js +43 -15
  7. package/lib/commands/run.js +2 -2
  8. package/lib/commands/update.js +2 -2
  9. package/lib/scaffold/CHANGELOG.json +14 -0
  10. package/lib/scaffold/backends/api/generator.js +14 -14
  11. package/lib/scaffold/backends/api/patch/README.md +83 -0
  12. package/lib/scaffold/backends/api/patch/lib/core/data/api/storage_api.dart +1 -1
  13. package/lib/scaffold/backends/api/patch/lib/core/data/entities/user_entity.dart +5 -0
  14. package/lib/scaffold/backends/api/patch/lib/{environnements.dart → environments.dart} +3 -11
  15. package/lib/scaffold/backends/api/patch/lib/features/ai_chat/api/ai_chat_api.dart +108 -0
  16. package/lib/scaffold/backends/api/patch/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +51 -0
  17. package/lib/scaffold/backends/api/patch/lib/features/{llm_chat/api/llm_chat_message_entity.dart → ai_chat/api/ai_chat_message_entity.dart} +5 -5
  18. package/lib/scaffold/backends/api/patch/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +71 -38
  19. package/lib/scaffold/backends/api/patch/lib/features/authentication/api/authentication_api.dart +2 -2
  20. package/lib/scaffold/backends/api/patch/lib/features/feedbacks/api/feature_request_api.dart +54 -0
  21. package/lib/scaffold/backends/api/patch/lib/features/onboarding/models/user_info.dart +16 -16
  22. package/lib/scaffold/backends/api/patch/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
  23. package/lib/scaffold/backends/api/patch/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
  24. package/lib/scaffold/backends/api/patch/lib/features/settings/ui/components/admin/admin_users_api.dart +100 -0
  25. package/lib/scaffold/backends/api/patch/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +13 -0
  26. package/lib/scaffold/backends/api/patch/lib/features/subscriptions/api/stripe_backend_api.dart +60 -0
  27. package/lib/scaffold/backends/api/patch/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
  28. package/lib/scaffold/backends/api/pubspec.yaml.tpl +4 -5
  29. package/lib/scaffold/backends/firebase/deploy.js +87 -13
  30. package/lib/scaffold/backends/firebase/enable-auth-via-cli.js +14 -6
  31. package/lib/scaffold/backends/firebase/generator.js +5 -5
  32. package/lib/scaffold/backends/firebase/setup-from-scratch.js +69 -45
  33. package/lib/scaffold/backends/firebase/tokens.js +4 -4
  34. package/lib/scaffold/backends/supabase/deploy.js +63 -11
  35. package/lib/scaffold/backends/supabase/edge-functions/admin-list-users/index.ts +149 -0
  36. package/lib/scaffold/backends/supabase/edge-functions/{llm-chat → ai-chat}/index.ts +19 -19
  37. package/lib/scaffold/backends/supabase/edge-functions/revenuecat-webhook/index.ts +2 -0
  38. package/lib/scaffold/backends/supabase/edge-functions/stripe-create-checkout-session/index.ts +123 -0
  39. package/lib/scaffold/backends/supabase/edge-functions/stripe-create-portal-session/index.ts +97 -0
  40. package/lib/scaffold/backends/supabase/edge-functions/stripe-list-prices/index.ts +83 -0
  41. package/lib/scaffold/backends/supabase/edge-functions/stripe-webhook/index.ts +138 -0
  42. package/lib/scaffold/backends/supabase/generator.js +17 -17
  43. package/lib/scaffold/backends/supabase/migrations/20240101000009_ai_messages.sql +50 -0
  44. package/lib/scaffold/backends/supabase/migrations/20240101000012_stripe_customers.sql +36 -0
  45. package/lib/scaffold/backends/supabase/migrations/20240101000013_admin_role.sql +62 -0
  46. package/lib/scaffold/backends/supabase/patch/lib/core/data/entities/user_entity.dart +4 -0
  47. package/lib/scaffold/backends/supabase/patch/lib/{environnements.dart → environments.dart} +3 -13
  48. package/lib/scaffold/backends/supabase/patch/lib/features/ai_chat/api/ai_chat_api.dart +95 -0
  49. package/lib/scaffold/backends/supabase/patch/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +52 -0
  50. package/lib/scaffold/backends/supabase/patch/lib/features/{llm_chat/api/llm_chat_message_entity.dart → ai_chat/api/ai_chat_message_entity.dart} +6 -6
  51. package/lib/scaffold/backends/supabase/patch/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +63 -35
  52. package/lib/scaffold/backends/supabase/patch/lib/features/authentication/api/authentication_api.dart +1 -1
  53. package/lib/scaffold/backends/supabase/patch/lib/features/feedbacks/api/feature_request_api.dart +46 -0
  54. package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/models/user_info.dart +16 -16
  55. package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
  56. package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
  57. package/lib/scaffold/backends/supabase/patch/lib/features/settings/ui/components/admin/admin_users_api.dart +93 -0
  58. package/lib/scaffold/backends/supabase/patch/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +13 -0
  59. package/lib/scaffold/backends/supabase/patch/lib/features/subscriptions/api/stripe_backend_api.dart +52 -0
  60. package/lib/scaffold/backends/supabase/patch/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
  61. package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +4 -5
  62. package/lib/scaffold/backends/supabase/tokens.js +3 -3
  63. package/lib/scaffold/catalog.js +9 -11
  64. package/lib/scaffold/generate.js +45 -31
  65. package/lib/scaffold/shared/generator-utils.js +188 -81
  66. package/lib/scaffold/shared/sort-imports.js +191 -0
  67. package/lib/scaffold/shared/template-strings.js +3 -3
  68. package/lib/utils/checks.js +2 -2
  69. package/lib/utils/i18n/messages-en.js +50 -35
  70. package/lib/utils/i18n/messages-es.js +50 -35
  71. package/lib/utils/i18n/messages-pt.js +52 -37
  72. package/lib/utils/updates.js +15 -15
  73. package/package.json +1 -1
  74. package/templates/firebase/.env.example +2 -2
  75. package/templates/firebase/android/app/src/main/res/drawable/background.png +0 -0
  76. package/templates/firebase/android/app/src/main/res/drawable-night/background.png +0 -0
  77. package/templates/firebase/android/app/src/main/res/drawable-night-v21/background.png +0 -0
  78. package/templates/firebase/android/app/src/main/res/drawable-v21/background.png +0 -0
  79. package/templates/firebase/android/app/src/main/res/values-night-v31/styles.xml +1 -1
  80. package/templates/firebase/android/app/src/main/res/values-v31/styles.xml +1 -1
  81. package/templates/firebase/assets/images/logo_wordmark_dark.png +0 -0
  82. package/templates/firebase/assets/images/logo_wordmark_light.png +0 -0
  83. package/templates/firebase/docs/revenuecat-setup.es.md +1 -1
  84. package/templates/firebase/docs/revenuecat-setup.pt.md +1 -1
  85. package/templates/firebase/firestore.rules +24 -5
  86. package/templates/firebase/functions/package-lock.json +22 -1
  87. package/templates/firebase/functions/package.json +2 -1
  88. package/templates/firebase/functions/src/admin/functions.ts +113 -0
  89. package/templates/firebase/functions/src/{llm_chat → ai_chat}/index.ts +16 -16
  90. package/templates/firebase/functions/src/index.ts +8 -2
  91. package/templates/firebase/functions/src/notifications/device_triggers.ts +2 -2
  92. package/templates/firebase/functions/src/notifications/triggers.ts +3 -3
  93. package/templates/firebase/functions/src/subscriptions/models/subscription_status.ts +2 -0
  94. package/templates/firebase/functions/src/subscriptions/stripe_functions.ts +222 -0
  95. package/templates/firebase/functions/src/subscriptions/subscriptions_functions.ts +2 -2
  96. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png +0 -0
  97. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png +0 -0
  98. package/templates/firebase/lib/components/components.dart +4 -1
  99. package/templates/firebase/lib/components/kasy_app_bar.dart +22 -7
  100. package/templates/firebase/lib/components/kasy_avatar.dart +7 -6
  101. package/templates/firebase/lib/components/kasy_button.dart +23 -99
  102. package/templates/firebase/lib/components/kasy_dialog.dart +11 -11
  103. package/templates/firebase/lib/components/kasy_image_viewer.dart +84 -0
  104. package/templates/firebase/lib/components/{kasy_sidebar_pro.dart → kasy_sidebar.dart} +692 -425
  105. package/templates/firebase/lib/components/kasy_status_tag.dart +91 -0
  106. package/templates/firebase/lib/components/kasy_text_area.dart +1 -3
  107. package/templates/firebase/lib/components/kasy_text_field.dart +5 -6
  108. package/templates/firebase/lib/components/kasy_text_field_otp.dart +1 -3
  109. package/templates/firebase/lib/components/kasy_toast.dart +2 -2
  110. package/templates/firebase/lib/components/kasy_web_header.dart +209 -0
  111. package/templates/firebase/lib/core/ads/ads_provider.dart +1 -1
  112. package/templates/firebase/lib/core/bottom_menu/bottom_menu.dart +57 -4
  113. package/templates/firebase/lib/core/bottom_menu/bottom_router.dart +19 -91
  114. package/templates/firebase/lib/core/bottom_menu/kasy_bottom_bar_factory.dart +196 -96
  115. package/templates/firebase/lib/core/bottom_menu/web_content_wrapper.dart +41 -0
  116. package/templates/firebase/lib/core/config/app_env.dart +5 -11
  117. package/templates/firebase/lib/core/config/features.dart +5 -4
  118. package/templates/firebase/lib/core/data/api/analytics_api.dart +1 -1
  119. package/templates/firebase/lib/core/data/api/http_client.dart +1 -1
  120. package/templates/firebase/lib/core/data/entities/user_entity.dart +3 -0
  121. package/templates/firebase/lib/core/data/models/entitlement.dart +35 -0
  122. package/templates/firebase/lib/core/data/models/subscription.dart +13 -186
  123. package/templates/firebase/lib/core/data/models/user.dart +11 -0
  124. package/templates/firebase/lib/core/data/repositories/user_repository.dart +1 -1
  125. package/templates/firebase/lib/core/home_widgets/home_widget_background_task.dart +1 -1
  126. package/templates/firebase/lib/core/home_widgets/home_widget_mywidget_service.dart +1 -1
  127. package/templates/firebase/lib/core/icons/kasy_icons.dart +31 -1
  128. package/templates/firebase/lib/core/rating/api/rating_api.dart +2 -2
  129. package/templates/firebase/lib/core/states/logout_action.dart +25 -0
  130. package/templates/firebase/lib/core/states/user_state_notifier.dart +3 -3
  131. package/templates/firebase/lib/core/theme/colors.dart +488 -188
  132. package/templates/firebase/lib/core/theme/radius.dart +22 -11
  133. package/templates/firebase/lib/core/theme/shadows.dart +66 -0
  134. package/templates/firebase/lib/core/theme/texts.dart +75 -41
  135. package/templates/firebase/lib/core/theme/universal_theme.dart +9 -4
  136. package/templates/firebase/lib/core/web_device_preview/web_device_preview.dart +5 -4
  137. package/templates/firebase/lib/core/web_viewport_scale.dart +52 -0
  138. package/templates/firebase/lib/core/widgets/kasy_brand_badge.dart +118 -0
  139. package/templates/firebase/lib/core/widgets/kasy_brand_logo.dart +40 -0
  140. package/templates/firebase/lib/core/widgets/kasy_focus_ring.dart +125 -0
  141. package/templates/firebase/lib/core/widgets/kasy_hover.dart +33 -13
  142. package/templates/firebase/lib/core/widgets/kasy_pressable_depth.dart +18 -13
  143. package/templates/firebase/lib/{environnements.dart → environments.dart} +3 -14
  144. package/templates/firebase/lib/features/ai_chat/ai_chat_page.dart +159 -0
  145. package/templates/firebase/lib/features/ai_chat/api/ai_chat_api.dart +107 -0
  146. package/templates/firebase/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +64 -0
  147. package/templates/firebase/lib/features/{llm_chat/api/llm_chat_message_entity.dart → ai_chat/api/ai_chat_message_entity.dart} +6 -6
  148. package/templates/firebase/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +73 -38
  149. package/templates/firebase/lib/features/ai_chat/providers/ai_conversations_notifier.dart +103 -0
  150. package/templates/firebase/lib/features/{llm_chat/ui/widgets/llm_chat_avatars.dart → ai_chat/ui/widgets/ai_chat_avatars.dart} +13 -13
  151. package/templates/firebase/lib/features/{llm_chat/ui/widgets/llm_chat_composer.dart → ai_chat/ui/widgets/ai_chat_composer.dart} +10 -10
  152. package/templates/firebase/lib/features/{llm_chat/llm_chat_page.dart → ai_chat/ui/widgets/ai_chat_conversation_view.dart} +80 -67
  153. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_list.dart +285 -0
  154. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_tile.dart +163 -0
  155. package/templates/firebase/lib/features/authentication/api/authentication_api.dart +52 -13
  156. package/templates/firebase/lib/features/authentication/api/popup_dismiss_watcher.dart +12 -0
  157. package/templates/firebase/lib/features/authentication/api/popup_dismiss_watcher_web.dart +35 -0
  158. package/templates/firebase/lib/features/authentication/ui/recover_password_page.dart +108 -68
  159. package/templates/firebase/lib/features/authentication/ui/signin_page.dart +38 -51
  160. package/templates/firebase/lib/features/authentication/ui/signup_page.dart +38 -51
  161. package/templates/firebase/lib/features/authentication/ui/widgets/auth_card_scaffold.dart +118 -0
  162. package/templates/firebase/lib/features/authentication/ui/widgets/recover_password_result.dart +61 -44
  163. package/templates/firebase/lib/features/feedbacks/api/feature_request_api.dart +32 -0
  164. package/templates/firebase/lib/features/feedbacks/models/feedback_state.dart +5 -5
  165. package/templates/firebase/lib/features/feedbacks/providers/feedback_page_notifier.dart +2 -2
  166. package/templates/firebase/lib/features/home/design_system_page.dart +808 -170
  167. package/templates/firebase/lib/features/home/home_components_page.dart +6 -3
  168. package/templates/firebase/lib/features/home/home_components_preview_page.dart +6 -6
  169. package/templates/firebase/lib/features/home/home_components_preview_registry.dart +325 -186
  170. package/templates/firebase/lib/features/home/home_feed.dart +289 -0
  171. package/templates/firebase/lib/features/home/home_image_grid.dart +355 -0
  172. package/templates/firebase/lib/features/home/home_page.dart +11 -250
  173. package/templates/firebase/lib/features/{local_reminder → local_reminders}/providers/reminder_notifier.dart +1 -1
  174. package/templates/firebase/lib/features/{local_reminder → local_reminders}/ui/reminder_page.dart +2 -2
  175. package/templates/firebase/lib/features/notifications/shared/att_permission.dart +1 -1
  176. package/templates/firebase/lib/features/notifications/ui/request_notification_permission.dart +25 -61
  177. package/templates/firebase/lib/features/notifications/ui/widgets/permission_request_view.dart +117 -0
  178. package/templates/firebase/lib/features/onboarding/models/user_info.dart +16 -16
  179. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
  180. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_features.dart +7 -9
  181. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_loader.dart +71 -48
  182. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_notifications_setup.dart +3 -2
  183. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_questions.dart +5 -5
  184. package/templates/firebase/lib/features/onboarding/ui/onboarding_page.dart +4 -4
  185. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_background.dart +4 -2
  186. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_feature.dart +39 -121
  187. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_illustration_scaffold.dart +105 -70
  188. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_module_mockups.dart +639 -0
  189. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_progress.dart +62 -50
  190. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
  191. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_step_header.dart +75 -0
  192. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_sticky_footer.dart +16 -5
  193. package/templates/firebase/lib/features/onboarding/ui/widgets/selectable_row_tile.dart +26 -22
  194. package/templates/firebase/lib/features/settings/settings_page.dart +601 -90
  195. package/templates/firebase/lib/features/settings/ui/components/admin/admin_page.dart +1193 -0
  196. package/templates/firebase/lib/features/settings/ui/components/admin/admin_paywalls.dart +1 -1
  197. package/templates/firebase/lib/features/settings/ui/components/admin/admin_routes.dart +2 -3
  198. package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_api.dart +86 -0
  199. package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_tab.dart +1215 -0
  200. package/templates/firebase/lib/features/settings/ui/components/admin/send_push_notification_page.dart +236 -0
  201. package/templates/firebase/lib/features/settings/ui/components/avatar_component.dart +3 -3
  202. package/templates/firebase/lib/features/settings/ui/widgets/kasy_user_avatar.dart +77 -0
  203. package/templates/firebase/lib/features/settings/ui/widgets/settings_tile.dart +1 -0
  204. package/templates/firebase/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +17 -0
  205. package/templates/firebase/lib/features/{subscription → subscriptions}/api/inapp_subscription_api.dart +67 -46
  206. package/templates/firebase/lib/features/subscriptions/api/revenuecat_product.dart +189 -0
  207. package/templates/firebase/lib/features/subscriptions/api/stripe_backend_api.dart +55 -0
  208. package/templates/firebase/lib/features/subscriptions/api/stripe_payment_api.dart +82 -0
  209. package/templates/firebase/lib/features/subscriptions/api/stripe_product.dart +178 -0
  210. package/templates/firebase/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
  211. package/templates/firebase/lib/features/subscriptions/api/subscription_payment_api.dart +53 -0
  212. package/templates/firebase/lib/features/subscriptions/api/subscription_payment_api_provider.dart +21 -0
  213. package/templates/firebase/lib/features/{subscription → subscriptions}/providers/premium_page_provider.dart +9 -6
  214. package/templates/firebase/lib/features/{subscription → subscriptions}/repositories/subscription_repository.dart +26 -30
  215. package/{lib/scaffold/backends/supabase/patch/lib/features/subscription → templates/firebase/lib/features/subscriptions}/shared/maybeshow_premium.dart +0 -2
  216. package/templates/firebase/lib/features/subscriptions/shared/subscription_management.dart +45 -0
  217. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/active_premium_content.dart +28 -4
  218. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_minimal.dart +7 -7
  219. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_row.dart +8 -8
  220. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_with_switch.dart +7 -7
  221. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/premium_content.dart +7 -7
  222. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/premium_page_factory.dart +5 -5
  223. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/premium_page.dart +5 -5
  224. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/comparison_table.dart +1 -1
  225. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/paywall_empty_state.dart +4 -4
  226. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_bottom_menu.dart +3 -4
  227. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/selectable_col.dart +1 -1
  228. package/templates/firebase/lib/i18n/en.i18n.json +171 -46
  229. package/templates/firebase/lib/i18n/es.i18n.json +175 -50
  230. package/templates/firebase/lib/i18n/pt.i18n.json +166 -41
  231. package/templates/firebase/lib/main.dart +6 -3
  232. package/templates/firebase/lib/router.dart +15 -23
  233. package/templates/firebase/pubspec.yaml +4 -5
  234. package/templates/firebase/test/core/data/repositories/user_repository_test.dart +3 -3
  235. package/templates/firebase/test/core/states/user_state_notifier_test.dart +4 -4
  236. package/templates/firebase/test/core/widgets/focus_ring_shape_test.dart +55 -0
  237. package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_inapp_subscription_api.dart +11 -4
  238. package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_revenuecat_product.dart +1 -0
  239. package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_subscription_api.dart +2 -2
  240. package/templates/firebase/test/features/{subscription → subscriptions}/subscription_page_test.dart +4 -4
  241. package/templates/firebase/test/test_utils.dart +6 -6
  242. package/templates/firebase/web/index.html +5 -2
  243. package/lib/scaffold/backends/api/patch/lib/features/llm_chat/api/llm_chat_api.dart +0 -58
  244. package/lib/scaffold/backends/api/patch/lib/features/subscription/shared/maybeshow_premium.dart +0 -86
  245. package/lib/scaffold/backends/supabase/migrations/20240101000009_llm_messages.sql +0 -22
  246. package/lib/scaffold/backends/supabase/patch/lib/features/llm_chat/api/llm_chat_api.dart +0 -47
  247. package/templates/firebase/assets/images/onboarding/authentication-login-template.jpg +0 -0
  248. package/templates/firebase/assets/images/onboarding/img2.jpg +0 -0
  249. package/templates/firebase/assets/images/onboarding/img3.jpg +0 -0
  250. package/templates/firebase/assets/images/onboarding/notifications.png +0 -0
  251. package/templates/firebase/assets/images/onboarding/purchase.png +0 -0
  252. package/templates/firebase/lib/core/sidebar/kasy_sidebar.dart +0 -2021
  253. package/templates/firebase/lib/features/authentication/ui/widgets/auth_brand.dart +0 -35
  254. package/templates/firebase/lib/features/home/home_features_page.dart +0 -207
  255. package/templates/firebase/lib/features/llm_chat/api/llm_chat_api.dart +0 -50
  256. package/templates/firebase/lib/features/settings/ui/components/admin/admin_bottom_sheet.dart +0 -316
  257. package/templates/firebase/lib/features/subscription/shared/maybeshow_premium.dart +0 -85
  258. /package/templates/firebase/lib/features/{local_reminder → local_reminders}/repositories/reminder_preferences.dart +0 -0
  259. /package/templates/firebase/lib/features/{subscription → subscriptions}/providers/models/premium_state.dart +0 -0
  260. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/feature_line.dart +0 -0
  261. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_background.dart +0 -0
  262. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_background_gradient.dart +0 -0
  263. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_banner.dart +0 -0
  264. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_card.dart +0 -0
  265. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_close_button.dart +0 -0
  266. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_feature.dart +0 -0
  267. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/selectable_row.dart +0 -0
  268. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/trial_switcher.dart +0 -0
  269. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/unsubscribe_feedback_popup.dart +0 -0
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Shared utilities for all backend generators.
3
- * Single source of truth for dart-defines, launch.json, .env.example, environnements.
3
+ * Single source of truth for dart-defines, launch.json, .env.example, environments.
4
4
  *
5
5
  * @module scaffold/shared/generator-utils
6
6
  */
@@ -75,11 +75,6 @@ function buildDartDefines(backend, modules, answers) {
75
75
  dev.push(`--dart-define=RC_IOS_API_KEY=${iosDefault}`);
76
76
  prod.push(`--dart-define=RC_ANDROID_API_KEY=${androidDefault}`);
77
77
  prod.push(`--dart-define=RC_IOS_API_KEY=${iosDefault}`);
78
- if (answers.revenuecatWeb) {
79
- const webKey = answers.rcWebKey || 'YOUR_REVENUECAT_WEB_KEY';
80
- dev.push(`--dart-define=RC_WEB_API_KEY=${webKey}`);
81
- prod.push(`--dart-define=RC_WEB_API_KEY=${webKey}`);
82
- }
83
78
  }
84
79
  if (modules.includes('sentry')) {
85
80
  const dsn = answers.sentryDsn || 'YOUR_SENTRY_DSN';
@@ -90,22 +85,22 @@ function buildDartDefines(backend, modules, answers) {
90
85
  dev.push(`--dart-define=MIXPANEL_TOKEN=${token}`);
91
86
  prod.push(`--dart-define=MIXPANEL_TOKEN=${token}`);
92
87
  }
93
- if (modules.includes('llm_chat')) {
94
- let endpoint = answers.llmChatEndpoint || '';
88
+ if (modules.includes('ai_chat')) {
89
+ let endpoint = answers.aiChatEndpoint || '';
95
90
  if (!endpoint) {
96
91
  if (backend === 'supabase' && answers.supabaseUrl) {
97
- endpoint = `${answers.supabaseUrl}/functions/v1/llm-chat`;
92
+ endpoint = `${answers.supabaseUrl}/functions/v1/ai-chat`;
98
93
  } else if (backend === 'api') {
99
- endpoint = answers.apiBaseUrl ? `${answers.apiBaseUrl}/llm-chat` : 'YOUR_LLM_ENDPOINT';
94
+ endpoint = answers.apiBaseUrl ? `${answers.apiBaseUrl}/ai-chat` : 'YOUR_AI_ENDPOINT';
100
95
  } else {
101
96
  // firebase — use the region configured for this project
102
97
  const projectId = answers.firebaseProjectId || 'YOUR_PROJECT_ID';
103
98
  const region = answers.functionsRegion || 'us-central1';
104
- endpoint = `https://${region}-${projectId}.cloudfunctions.net/llmChat`;
99
+ endpoint = `https://${region}-${projectId}.cloudfunctions.net/aiChat`;
105
100
  }
106
101
  }
107
- dev.push(`--dart-define=LLM_CHAT_ENDPOINT=${endpoint}`);
108
- prod.push(`--dart-define=LLM_CHAT_ENDPOINT=${endpoint}`);
102
+ dev.push(`--dart-define=AI_CHAT_ENDPOINT=${endpoint}`);
103
+ prod.push(`--dart-define=AI_CHAT_ENDPOINT=${endpoint}`);
109
104
  }
110
105
 
111
106
  return { dev, prod };
@@ -178,10 +173,10 @@ async function writeEnvExample(projectDir, modules, answers, language = 'en') {
178
173
  if (t.revenuecatProd) lines.push(t.revenuecatProd);
179
174
  lines.push(`RC_IOS_PROD_KEY=${(answers.rcIosProdKey || '').trim()}`);
180
175
  lines.push(`RC_ANDROID_PROD_KEY=${(answers.rcAndroidProdKey || '').trim()}`);
181
- if (answers.revenuecatWeb) {
182
- lines.push(`RC_WEB_API_KEY=${answers.rcWebKey || 'YOUR_REVENUECAT_WEB_KEY'}`);
183
- }
184
176
  }
177
+ // NOTE: the Stripe module needs NO client-side env. Hosted Checkout + Customer
178
+ // Portal use only server-side secrets (STRIPE_SECRET_KEY / STRIPE_WEBHOOK_SECRET),
179
+ // which live on the backend (Firebase/Supabase secrets), never in the app.
185
180
  if (modules.includes('sentry')) {
186
181
  lines.push('');
187
182
  lines.push(t.sentry);
@@ -192,10 +187,10 @@ async function writeEnvExample(projectDir, modules, answers, language = 'en') {
192
187
  lines.push(t.mixpanel);
193
188
  lines.push(`MIXPANEL_TOKEN=${answers.mixpanelToken || 'YOUR_MIXPANEL_TOKEN'}`);
194
189
  }
195
- if (modules.includes('llm_chat')) {
190
+ if (modules.includes('ai_chat')) {
196
191
  lines.push('');
197
- lines.push(t.llmChat);
198
- lines.push(`LLM_CHAT_ENDPOINT=${answers.llmChatEndpoint || 'YOUR_FUNCTION_URL'}`);
192
+ lines.push(t.aiChat);
193
+ lines.push(`AI_CHAT_ENDPOINT=${answers.aiChatEndpoint || 'YOUR_FUNCTION_URL'}`);
199
194
  }
200
195
 
201
196
  await fs.outputFile(path.join(projectDir, '.env.example'), lines.join('\n') + '\n', 'utf8');
@@ -263,15 +258,15 @@ async function cleanFirebaseJsonKitRefs(projectDir) {
263
258
  }
264
259
 
265
260
  /**
266
- * Write environnements.dart overrides for backend-specific config.
261
+ * Write environments.dart overrides for backend-specific config.
267
262
  *
268
263
  * @param {string} projectDir
269
264
  * @param {'firebase'|'supabase'|'api'} backend
270
265
  * @param {Record<string,string>} tokens
271
266
  * @param {object} answers - supabaseUrl, supabaseAnonKey, apiBaseUrl
272
267
  */
273
- async function writeEnvironnementsOverrides(projectDir, backend, tokens, answers) {
274
- const envPath = path.join(projectDir, 'lib', 'environnements.dart');
268
+ async function writeEnvironmentsOverrides(projectDir, backend, tokens, answers) {
269
+ const envPath = path.join(projectDir, 'lib', 'environments.dart');
275
270
  if (!(await fs.pathExists(envPath))) return;
276
271
 
277
272
  let content = await fs.readFile(envPath, 'utf8');
@@ -313,8 +308,8 @@ async function writeEnvironnementsOverrides(projectDir, backend, tokens, answers
313
308
  async function writeRouter(projectDir, modules, packageName, defaultPaywall = 'basic') {
314
309
  const pkg = packageName;
315
310
  const withFeedback = modules.includes('feedback');
316
- const withLlmChat = modules.includes('llm_chat');
317
- const withLocalNotifications = modules.includes('local_notifications');
311
+ const withAiChat = modules.includes('ai_chat');
312
+ const withLocalReminders = modules.includes('local_reminders');
318
313
  const withOnboarding = modules.includes('onboarding');
319
314
  const withRevenuecat = modules.includes('revenuecat');
320
315
  const withAnalytics = modules.includes('analytics');
@@ -347,12 +342,8 @@ async function writeRouter(projectDir, modules, packageName, defaultPaywall = 'b
347
342
  lines.push(`import 'package:${pkg}/features/home/home_components_page.dart';`);
348
343
  lines.push(`import 'package:${pkg}/features/home/home_components_preview_page.dart';`);
349
344
  lines.push(`import 'package:${pkg}/features/home/home_components_preview_registry.dart';`);
350
- lines.push(`import 'package:${pkg}/features/home/home_features_page.dart';`);
351
- if (withLlmChat) {
352
- lines.push(`import 'package:${pkg}/features/llm_chat/llm_chat_page.dart';`);
353
- }
354
- if (withLocalNotifications) {
355
- lines.push(`import 'package:${pkg}/features/local_reminder/ui/reminder_page.dart';`);
345
+ if (withLocalReminders) {
346
+ lines.push(`import 'package:${pkg}/features/local_reminders/ui/reminder_page.dart';`);
356
347
  }
357
348
  if (withOnboarding) {
358
349
  lines.push(`import 'package:${pkg}/features/onboarding/ui/onboarding_page.dart';`);
@@ -369,8 +360,8 @@ async function writeRouter(projectDir, modules, packageName, defaultPaywall = 'b
369
360
  lines.push(`import 'package:${pkg}/features/settings/ui/components/admin/admin_routes.dart';`);
370
361
  lines.push(`import 'package:${pkg}/features/settings/ui/components/admin/send_push_notification_page.dart';`);
371
362
  if (withRevenuecat) {
372
- lines.push(`import 'package:${pkg}/features/subscription/ui/component/premium_page_factory.dart';`);
373
- lines.push(`import 'package:${pkg}/features/subscription/ui/premium_page.dart';`);
363
+ lines.push(`import 'package:${pkg}/features/subscriptions/ui/component/premium_page_factory.dart';`);
364
+ lines.push(`import 'package:${pkg}/features/subscriptions/ui/premium_page.dart';`);
374
365
  }
375
366
  lines.push(`import 'package:logger/logger.dart';`);
376
367
 
@@ -427,11 +418,6 @@ async function writeRouter(projectDir, modules, packageName, defaultPaywall = 'b
427
418
  // full-screen, above the bottom bar, URL-addressable. Returning pops back to
428
419
  // the tab with its menu intact.
429
420
  lines.push(` GoRoute(`);
430
- lines.push(` name: 'features',`);
431
- lines.push(` path: '/features',`);
432
- lines.push(` builder: (context, state) => const HomeFeaturesPage(),`);
433
- lines.push(` ),`);
434
- lines.push(` GoRoute(`);
435
421
  lines.push(` name: 'design_system',`);
436
422
  lines.push(` path: '/design-system',`);
437
423
  lines.push(` builder: (context, state) => const DesignSystemPage(),`);
@@ -505,17 +491,8 @@ async function writeRouter(projectDir, modules, packageName, defaultPaywall = 'b
505
491
  lines.push(` ),`);
506
492
  }
507
493
 
508
- // LLM Chat
509
- if (withLlmChat) {
510
- lines.push(` GoRoute(`);
511
- lines.push(` name: 'assistant',`);
512
- lines.push(` path: '/assistant',`);
513
- lines.push(` builder: (context, state) => const LlmChatPage(),`);
514
- lines.push(` ),`);
515
- }
516
-
517
494
  // Local notifications reminder
518
- if (withLocalNotifications) {
495
+ if (withLocalReminders) {
519
496
  lines.push(` GoRoute(`);
520
497
  lines.push(` name: 'reminder',`);
521
498
  lines.push(` path: '/reminder',`);
@@ -610,34 +587,34 @@ async function writeRouter(projectDir, modules, packageName, defaultPaywall = 'b
610
587
  *
611
588
  * @param {string} projectDir
612
589
  * @param {string[]} modules
613
- * @param {object} [answers={}] - moduleAnswers (revenuecatWeb, etc.)
590
+ * @param {object} [answers={}] - moduleAnswers (rcTestKey, stripe keys, etc.)
614
591
  * @param {string} [language='en'] - User's CLI language (en, pt, es)
615
592
  */
616
593
  async function writeFeaturesConfig(projectDir, modules, answers = {}, language = 'en') {
617
594
  const withOnboarding = modules.includes('onboarding');
618
- const withLlmChat = modules.includes('llm_chat');
595
+ const withAiChat = modules.includes('ai_chat');
619
596
  const withFeedback = modules.includes('feedback');
620
597
  const withRevenuecat = modules.includes('revenuecat');
621
- const withLocalNotifications = modules.includes('local_notifications');
598
+ const withStripe = modules.includes('stripe');
599
+ const withLocalReminders = modules.includes('local_reminders');
622
600
  const withWeb = modules.includes('web');
623
- const revenuecatWeb = !!(modules.includes('revenuecat') && modules.includes('web') && answers.revenuecatWeb);
624
601
 
625
602
  const f = getStrings(language).features;
626
603
  const content = `${f.comment1}
627
604
  ${f.comment2}
628
605
  // ignore_for_file: avoid_redundant_argument_values
629
606
  const bool withOnboarding = ${withOnboarding};
630
- const bool withLlmChat = ${withLlmChat};
607
+ const bool withAiChat = ${withAiChat};
631
608
  const bool withFeedback = ${withFeedback};
632
609
  const bool withRevenuecat = ${withRevenuecat};
633
- const bool withLocalNotifications = ${withLocalNotifications};
610
+ // Stripe web subscriptions module (independent from RevenueCat mobile IAP).
611
+ const bool withStripe = ${withStripe};
612
+ const bool withLocalReminders = ${withLocalReminders};
634
613
  ${f.comment3}
635
614
  ${f.comment4}
636
615
  ${f.comment5}
637
616
  ${f.comment6}
638
617
  const bool withWeb = ${withWeb};
639
- ${f.comment7}
640
- const bool revenuecatWeb = ${revenuecatWeb};
641
618
  `;
642
619
  await fs.ensureDir(path.join(projectDir, 'lib', 'core', 'config'));
643
620
  await fs.outputFile(
@@ -655,7 +632,6 @@ const bool revenuecatWeb = ${revenuecatWeb};
655
632
  */
656
633
  async function writeKitSetup(projectDir, options) {
657
634
  const { appName, bundleId, backend, modules = [], firebaseProjectId, supabaseUrl, supabaseAnonKey, moduleAnswers = {} } = options;
658
- const revenuecatWeb = !!(modules.includes('revenuecat') && modules.includes('web') && moduleAnswers.revenuecatWeb);
659
635
  // Booleans tracking which RC keys the user configured. We never persist key
660
636
  // values to kit_setup.json — those live in .env (gitignored). These flags
661
637
  // let `kasy doctor` warn about release readiness without re-reading .env.
@@ -672,12 +648,12 @@ async function writeKitSetup(projectDir, options) {
672
648
  backendProvider: backend || 'firebase',
673
649
  functionsRegion: moduleAnswers.functionsRegion || 'us-central1',
674
650
  subscriptionModule: modules.includes('revenuecat'),
651
+ stripeModule: modules.includes('stripe'),
675
652
  feedbackModule: modules.includes('feedback'),
676
653
  useFirebaseAuth: true,
677
654
  useFirebaseFirestore: backend === 'firebase',
678
655
  storageProvider: backend === 'firebase' ? 'firebase' : backend === 'supabase' ? 'supabase' : 'api',
679
656
  webCompat: modules.includes('web'),
680
- revenuecatWeb,
681
657
  ...(revenuecatKeys ? { revenuecatKeys } : {}),
682
658
  internationalization: true,
683
659
  useSentry: modules.includes('sentry'),
@@ -910,7 +886,7 @@ async function writeMainDart(projectDir, backend, modules, packageName, options
910
886
  lines.push(`import 'package:${pkg}/core/dev_inspector/dev_inspector.dart';`);
911
887
  lines.push(`import 'package:${pkg}/core/theme/theme.dart';`);
912
888
  lines.push(`import 'package:${pkg}/core/web_device_preview/web_device_preview.dart';`);
913
- lines.push(`import 'package:${pkg}/environnements.dart';`);
889
+ lines.push(`import 'package:${pkg}/environments.dart';`);
914
890
  if (withFirebase) {
915
891
  lines.push(`import 'package:${pkg}/firebase_options_dev.dart' as firebase_dev;`);
916
892
  }
@@ -922,7 +898,7 @@ async function writeMainDart(projectDir, backend, modules, packageName, options
922
898
  }
923
899
  lines.push(`import 'package:${pkg}/features/notifications/repositories/notifications_repository.dart';`);
924
900
  if (withRevenuecat) {
925
- lines.push(`import 'package:${pkg}/features/subscription/repositories/subscription_repository.dart';`);
901
+ lines.push(`import 'package:${pkg}/features/subscriptions/repositories/subscription_repository.dart';`);
926
902
  }
927
903
  lines.push(`import 'package:${pkg}/router.dart';`);
928
904
  if (withSentry) {
@@ -972,7 +948,9 @@ async function writeMainDart(projectDir, backend, modules, packageName, options
972
948
  lines.push(` // initialize Supabase`);
973
949
  lines.push(` await Supabase.initialize(`);
974
950
  lines.push(` url: AppEnv.backendUrl,`);
975
- lines.push(` anonKey: AppEnv.supabaseToken,`);
951
+ // supabase_flutter ≥2.x: anonKey is deprecated in favor of publishableKey.
952
+ // Both feed the same effectiveKey, so the existing token value is unchanged.
953
+ lines.push(` publishableKey: AppEnv.supabaseToken,`);
976
954
  lines.push(` );`);
977
955
  lines.push('');
978
956
  }
@@ -1245,14 +1223,12 @@ async function writeMainDart(projectDir, backend, modules, packageName, options
1245
1223
  async function removeModuleDirs(projectDir, modules) {
1246
1224
  const removable = [
1247
1225
  { module: 'feedback', dir: path.join('lib', 'features', 'feedbacks') },
1248
- { module: 'llm_chat', dir: path.join('lib', 'features', 'llm_chat') },
1226
+ { module: 'ai_chat', dir: path.join('lib', 'features', 'ai_chat') },
1249
1227
  { module: 'onboarding',dir: path.join('lib', 'features', 'onboarding') },
1250
1228
  // home_widgets/ imports home_widget package — remove when widget not selected
1251
1229
  { module: 'widget', dir: path.join('lib', 'core', 'home_widgets') },
1252
- // subscription/ imports purchases_flutter — remove when revenuecat not selected
1253
- { module: 'revenuecat', dir: path.join('lib', 'features', 'subscription') },
1254
- // local_reminder/ uses flutter_timezone/timezone — remove when local_notifications not selected
1255
- { module: 'local_notifications', dir: path.join('lib', 'features', 'local_reminder') },
1230
+ // local_reminders/ uses flutter_timezone/timezone — remove when local_reminders not selected
1231
+ { module: 'local_reminders', dir: path.join('lib', 'features', 'local_reminders') },
1256
1232
  ];
1257
1233
 
1258
1234
  for (const { module, dir } of removable) {
@@ -1263,6 +1239,17 @@ async function removeModuleDirs(projectDir, modules) {
1263
1239
  }
1264
1240
  }
1265
1241
  }
1242
+
1243
+ // subscription/ is the payment CORE shared by RevenueCat (mobile) and Stripe
1244
+ // (web). Keep it when EITHER module is selected; remove only when neither is.
1245
+ // The RevenueCat-only files (purchases_flutter) are stripped separately by
1246
+ // writeStripeOnlySubscription when Stripe is selected without RevenueCat.
1247
+ if (!modules.includes('revenuecat') && !modules.includes('stripe')) {
1248
+ const subDir = path.join(projectDir, 'lib', 'features', 'subscriptions');
1249
+ if (await fs.pathExists(subDir)) {
1250
+ await fs.remove(subDir);
1251
+ }
1252
+ }
1266
1253
  }
1267
1254
 
1268
1255
  /**
@@ -1674,7 +1661,7 @@ sealed class Subscription with _$Subscription {
1674
1661
  // here) for source-compat with the bundled tests. ignore_for_file stops
1675
1662
  // `dart fix` from stripping them via avoid_unused_constructor_parameters.
1676
1663
  await fs.outputFile(
1677
- path.join(projectDir, 'lib', 'features', 'subscription', 'repositories', 'subscription_repository.dart'),
1664
+ path.join(projectDir, 'lib', 'features', 'subscriptions', 'repositories', 'subscription_repository.dart'),
1678
1665
  `// ignore_for_file: avoid_unused_constructor_parameters
1679
1666
  import 'package:flutter_riverpod/flutter_riverpod.dart';
1680
1667
  import 'package:${pkg}/core/data/models/subscription.dart';
@@ -1721,7 +1708,7 @@ class UserCancelledPurchaseException implements Exception {}
1721
1708
 
1722
1709
  // 2. No-op maybeshow_premium.dart (used by home_page — must implement MaybeShowWithRef)
1723
1710
  await fs.outputFile(
1724
- path.join(projectDir, 'lib', 'features', 'subscription', 'shared', 'maybeshow_premium.dart'),
1711
+ path.join(projectDir, 'lib', 'features', 'subscriptions', 'shared', 'maybeshow_premium.dart'),
1725
1712
  `import 'package:flutter_riverpod/flutter_riverpod.dart';
1726
1713
  import 'package:${pkg}/core/states/components/maybeshow_component.dart';
1727
1714
  import 'package:${pkg}/core/states/models/event_model.dart';
@@ -1736,7 +1723,7 @@ class MaybeShowPremiumPage implements MaybeShowWithRef {
1736
1723
 
1737
1724
  // 3. No-op premium_page.dart (used by onboarding_page with args: PremiumPageArgs(redirect:))
1738
1725
  await fs.outputFile(
1739
- path.join(projectDir, 'lib', 'features', 'subscription', 'ui', 'premium_page.dart'),
1726
+ path.join(projectDir, 'lib', 'features', 'subscriptions', 'ui', 'premium_page.dart'),
1740
1727
  `import 'package:flutter/material.dart';
1741
1728
  // No-op premium page. Run: kasy add revenuecat to activate.
1742
1729
  class PremiumPage extends StatelessWidget {
@@ -1759,7 +1746,7 @@ class PremiumPageArgs {
1759
1746
  // Mirror the constants declared in Firebase/.../premium_page_factory.dart so that
1760
1747
  // admin_routes.dart compiles even without revenuecat selected.
1761
1748
  await fs.outputFile(
1762
- path.join(projectDir, 'lib', 'features', 'subscription', 'ui', 'component', 'premium_page_factory.dart'),
1749
+ path.join(projectDir, 'lib', 'features', 'subscriptions', 'ui', 'component', 'premium_page_factory.dart'),
1763
1750
  `// No-op paywall factory. Run: kasy add revenuecat to activate.
1764
1751
  enum PaywallFactory { basic, basicRow, minimal, withSwitch }
1765
1752
  `,
@@ -1768,7 +1755,7 @@ enum PaywallFactory { basic, basicRow, minimal, withSwitch }
1768
1755
 
1769
1756
  // 5. No-op subscription_entity.dart (used by test_utils, fake_subscription_api)
1770
1757
  await fs.outputFile(
1771
- path.join(projectDir, 'lib', 'features', 'subscription', 'api', 'entities', 'subscription_entity.dart'),
1758
+ path.join(projectDir, 'lib', 'features', 'subscriptions', 'api', 'entities', 'subscription_entity.dart'),
1772
1759
  `// ignore_for_file: constant_identifier_names
1773
1760
  // No-op subscription entity. Run: kasy add revenuecat to activate.
1774
1761
  enum SubscriptionStatus { ACTIVE, PAUSED, EXPIRED, LIFETIME, CANCELLED }
@@ -1793,9 +1780,9 @@ class SubscriptionEntity {
1793
1780
 
1794
1781
  // 6. No-op subscription_api.dart (provider + abstract class used by test_utils and fake)
1795
1782
  await fs.outputFile(
1796
- path.join(projectDir, 'lib', 'features', 'subscription', 'api', 'subscription_api.dart'),
1783
+ path.join(projectDir, 'lib', 'features', 'subscriptions', 'api', 'subscription_api.dart'),
1797
1784
  `import 'package:flutter_riverpod/flutter_riverpod.dart';
1798
- import 'package:${pkg}/features/subscription/api/entities/subscription_entity.dart';
1785
+ import 'package:${pkg}/features/subscriptions/api/entities/subscription_entity.dart';
1799
1786
  // No-op subscription API. Run: kasy add revenuecat to activate.
1800
1787
  final subscriptionApiProvider = Provider<SubscriptionApi>(
1801
1788
  (ref) => _NoOpSubscriptionApi(),
@@ -1815,7 +1802,7 @@ class _NoOpSubscriptionApi implements SubscriptionApi {
1815
1802
 
1816
1803
  // 7. No-op inapp_subscription_api.dart (provider + class used by test_utils and fake)
1817
1804
  await fs.outputFile(
1818
- path.join(projectDir, 'lib', 'features', 'subscription', 'api', 'inapp_subscription_api.dart'),
1805
+ path.join(projectDir, 'lib', 'features', 'subscriptions', 'api', 'inapp_subscription_api.dart'),
1819
1806
  `import 'package:flutter_riverpod/flutter_riverpod.dart';
1820
1807
  import 'package:${pkg}/core/data/models/subscription.dart';
1821
1808
  // No-op in-app subscription API. Run: kasy add revenuecat to activate.
@@ -1841,17 +1828,17 @@ class RevenueCatPaymentApi {
1841
1828
  'utf8',
1842
1829
  );
1843
1830
 
1844
- // 8. Remove test/features/subscription/ entirely (imports purchases_flutter and RevenueCat types)
1845
- const testSubscriptionDir = path.join(projectDir, 'test', 'features', 'subscription');
1831
+ // 8. Remove test/features/subscriptions/ entirely (imports purchases_flutter and RevenueCat types)
1832
+ const testSubscriptionDir = path.join(projectDir, 'test', 'features', 'subscriptions');
1846
1833
  if (await fs.pathExists(testSubscriptionDir)) {
1847
1834
  await fs.remove(testSubscriptionDir);
1848
1835
  }
1849
1836
 
1850
1837
  // 9. Restore fake_subscription_api.dart with currentFake setter (required by test_utils.dart)
1851
1838
  await fs.outputFile(
1852
- path.join(projectDir, 'test', 'features', 'subscription', 'api', 'fake_subscription_api.dart'),
1853
- `import 'package:${pkg}/features/subscription/api/entities/subscription_entity.dart';
1854
- import 'package:${pkg}/features/subscription/api/subscription_api.dart';
1839
+ path.join(projectDir, 'test', 'features', 'subscriptions', 'api', 'fake_subscription_api.dart'),
1840
+ `import 'package:${pkg}/features/subscriptions/api/entities/subscription_entity.dart';
1841
+ import 'package:${pkg}/features/subscriptions/api/subscription_api.dart';
1855
1842
  // No-op subscription API fake. Run: kasy add revenuecat to activate.
1856
1843
  class SubscriptionApiFake implements SubscriptionApi {
1857
1844
  SubscriptionEntity? currentFake;
@@ -1865,8 +1852,8 @@ class SubscriptionApiFake implements SubscriptionApi {
1865
1852
 
1866
1853
  // 10. Restore fake_inapp_subscription_api.dart without purchases_flutter dependency
1867
1854
  await fs.outputFile(
1868
- path.join(projectDir, 'test', 'features', 'subscription', 'api', 'fake_inapp_subscription_api.dart'),
1869
- `import 'package:${pkg}/features/subscription/api/inapp_subscription_api.dart';
1855
+ path.join(projectDir, 'test', 'features', 'subscriptions', 'api', 'fake_inapp_subscription_api.dart'),
1856
+ `import 'package:${pkg}/features/subscriptions/api/inapp_subscription_api.dart';
1870
1857
  // No-op in-app subscription API fake. Run: kasy add revenuecat to activate.
1871
1858
  class InAppSubscriptionApiFake extends RevenueCatPaymentApi {}
1872
1859
  `,
@@ -1874,6 +1861,125 @@ class InAppSubscriptionApiFake extends RevenueCatPaymentApi {}
1874
1861
  );
1875
1862
  }
1876
1863
 
1864
+ /**
1865
+ * Stripe-without-RevenueCat: keep the subscription CORE but strip the
1866
+ * RevenueCat (purchases_flutter) files so the app compiles without the
1867
+ * RevenueCat SDK. Called when the Stripe module is selected and RevenueCat is not.
1868
+ *
1869
+ * @param {string} projectDir
1870
+ * @param {string} packageName
1871
+ */
1872
+ async function writeStripeOnlySubscription(projectDir, packageName) {
1873
+ const pkg = packageName;
1874
+ const subApiDir = path.join(projectDir, 'lib', 'features', 'subscriptions', 'api');
1875
+
1876
+ // 1. Remove the RevenueCat-only payment files (they import purchases_flutter).
1877
+ for (const f of ['inapp_subscription_api.dart', 'revenuecat_product.dart']) {
1878
+ const p = path.join(subApiDir, f);
1879
+ if (await fs.pathExists(p)) await fs.remove(p);
1880
+ }
1881
+
1882
+ // 2. Rewrite the injection provider to a Stripe-only version (no RevenueCat import).
1883
+ await fs.outputFile(
1884
+ path.join(subApiDir, 'subscription_payment_api_provider.dart'),
1885
+ `import 'package:flutter_riverpod/flutter_riverpod.dart';
1886
+ import 'package:${pkg}/features/subscriptions/api/stripe_backend_api.dart';
1887
+ import 'package:${pkg}/features/subscriptions/api/stripe_payment_api.dart';
1888
+ import 'package:${pkg}/features/subscriptions/api/subscription_payment_api.dart';
1889
+
1890
+ /// Stripe-only subscription provider (web payments). Generated by the CLI for a
1891
+ /// project that enabled the Stripe module without RevenueCat, so it never
1892
+ /// references the RevenueCat SDK (purchases_flutter).
1893
+ final inAppSubscriptionApiProvider = Provider<SubscriptionPaymentApi>((ref) {
1894
+ return StripePaymentApi(backend: ref.read(stripeBackendApiProvider));
1895
+ });
1896
+ `,
1897
+ 'utf8',
1898
+ );
1899
+
1900
+ // 3. Strip the RevenueCat device-id calls from tracking_api.dart when the
1901
+ // Facebook module kept the real file (it imports purchases_flutter).
1902
+ const trackingFile = path.join(projectDir, 'lib', 'core', 'data', 'api', 'tracking_api.dart');
1903
+ if (await fs.pathExists(trackingFile)) {
1904
+ let tracking = await fs.readFile(trackingFile, 'utf8');
1905
+ if (tracking.includes('purchases_flutter')) {
1906
+ tracking = tracking
1907
+ .replace("import 'package:purchases_flutter/purchases_flutter.dart';\n", '')
1908
+ .replace(/\n?[ \t]*\/\/ collect identifiers for REVENUECAT\n[ \t]*await Purchases\.collectDeviceIdentifiers\(\);/, '')
1909
+ .replace(
1910
+ /[ \t]*if \(userAnonId != null\) \{\n[ \t]*await Purchases\.setFBAnonymousID\(userAnonId\);\n[ \t]*\}/,
1911
+ ' // RevenueCat device-id sync removed (no RevenueCat module).',
1912
+ );
1913
+ await fs.writeFile(trackingFile, tracking, 'utf8');
1914
+ }
1915
+ }
1916
+
1917
+ // 4. Tests: drop the RevenueCat-specific files and replace the in-app fake
1918
+ // with a no-op implementing the payment interface (no purchases_flutter).
1919
+ const testApiDir = path.join(projectDir, 'test', 'features', 'subscriptions', 'api');
1920
+ const fakeRcProduct = path.join(testApiDir, 'fake_revenuecat_product.dart');
1921
+ if (await fs.pathExists(fakeRcProduct)) await fs.remove(fakeRcProduct);
1922
+ const pageTest = path.join(projectDir, 'test', 'features', 'subscriptions', 'subscription_page_test.dart');
1923
+ if (await fs.pathExists(pageTest)) await fs.remove(pageTest);
1924
+
1925
+ if (await fs.pathExists(testApiDir)) {
1926
+ await fs.outputFile(
1927
+ path.join(testApiDir, 'fake_inapp_subscription_api.dart'),
1928
+ `import 'package:${pkg}/core/data/models/entitlement.dart';
1929
+ import 'package:${pkg}/core/data/models/subscription.dart';
1930
+ import 'package:${pkg}/features/subscriptions/api/entities/subscription_entity.dart';
1931
+ import 'package:${pkg}/features/subscriptions/api/subscription_payment_api.dart';
1932
+
1933
+ /// No-op payment API fake for a Stripe-only project (no RevenueCat SDK).
1934
+ /// Implements [SubscriptionPaymentApi] so the shared tests can override
1935
+ /// inAppSubscriptionApiProvider without depending on purchases_flutter.
1936
+ class InAppSubscriptionApiFake implements SubscriptionPaymentApi {
1937
+ String? userId;
1938
+ final List<SubscriptionProduct> activeSubscriptions = [];
1939
+
1940
+ @override
1941
+ Future<void> init() async {}
1942
+
1943
+ @override
1944
+ Future<void> initUser(String userId) async {
1945
+ this.userId = userId;
1946
+ }
1947
+
1948
+ @override
1949
+ Future<void> disconnectUser() async {}
1950
+
1951
+ @override
1952
+ Future<List<SubscriptionProduct>> getOffers(String? offerId) async => [];
1953
+
1954
+ @override
1955
+ Future<void> purchaseProduct(SubscriptionProduct product) async {
1956
+ activeSubscriptions.add(product);
1957
+ }
1958
+
1959
+ @override
1960
+ Future<void> unsubscribe(SubscriptionStore? origin) async {}
1961
+
1962
+ @override
1963
+ Future<void> restorePurchase() async {}
1964
+
1965
+ @override
1966
+ Future<List<Entitlement>> getPermissions() async => [];
1967
+
1968
+ @override
1969
+ Future<List<Entitlement>?> getEntitlements() async => [];
1970
+
1971
+ @override
1972
+ Future<SubscriptionProduct?> getFromProductId(String productId) async => null;
1973
+
1974
+ @override
1975
+ Future<void> presentCodeRedemptionSheet() async {}
1976
+ }
1977
+ `,
1978
+ 'utf8',
1979
+ );
1980
+ }
1981
+ }
1982
+
1877
1983
  /**
1878
1984
  * Remove DEVELOPMENT_TEAM entries from the generated iOS project.pbxproj.
1879
1985
  * Clients use their own Apple team — leaving the internal team ID would cause
@@ -1933,7 +2039,7 @@ module.exports = {
1933
2039
  writeEnvFileIfMissing,
1934
2040
  writeFirebaserc,
1935
2041
  cleanFirebaseJsonKitRefs,
1936
- writeEnvironnementsOverrides,
2042
+ writeEnvironmentsOverrides,
1937
2043
  writeFeaturesConfig,
1938
2044
  writeKitSetup,
1939
2045
  writeMakefile,
@@ -1948,6 +2054,7 @@ module.exports = {
1948
2054
  patchLanguageSwitcherNoWidget,
1949
2055
  writeNoOpFeatureRequestRepository,
1950
2056
  writeNoOpSubscriptionStubs,
2057
+ writeStripeOnlySubscription,
1951
2058
  writeNoOpSentryUsages,
1952
2059
  stripPubspecDeps,
1953
2060
  addPubspecDep,