vibefast-cli 1.2.1 → 1.3.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.
- package/README.md +30 -95
- package/dist/__tests__/recipes.test.js +94 -91
- package/dist/__tests__/recipes.test.js.map +1 -1
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +301 -125
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/checklist.d.ts.map +1 -1
- package/dist/commands/checklist.js +85 -44
- package/dist/commands/checklist.js.map +1 -1
- package/dist/commands/health.d.ts.map +1 -1
- package/dist/commands/health.js +13 -4
- package/dist/commands/health.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +118 -26
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/migrate.d.ts +3 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +202 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/remove.d.ts.map +1 -1
- package/dist/commands/remove.js +61 -3
- package/dist/commands/remove.js.map +1 -1
- package/dist/core/auth.d.ts.map +1 -1
- package/dist/core/auth.js +20 -18
- package/dist/core/auth.js.map +1 -1
- package/dist/core/codemod.d.ts +33 -0
- package/dist/core/codemod.d.ts.map +1 -1
- package/dist/core/codemod.js +116 -0
- package/dist/core/codemod.js.map +1 -1
- package/dist/core/detect.d.ts.map +1 -1
- package/dist/core/detect.js +24 -7
- package/dist/core/detect.js.map +1 -1
- package/dist/core/journal.d.ts +1 -0
- package/dist/core/journal.d.ts.map +1 -1
- package/dist/core/journal.js.map +1 -1
- package/dist/core/recipes.d.ts.map +1 -1
- package/dist/core/recipes.js +25 -7
- package/dist/core/recipes.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/docs/architecture.md +50 -0
- package/docs/commands.md +78 -0
- package/docs/contributing.md +27 -0
- package/docs/quickstart.md +50 -0
- package/docs/recipes.md +57 -0
- package/docs/troubleshooting.md +31 -0
- package/package.json +2 -2
- package/recipes/0/apps/native/src/components/advanced-ui/timeline/demo.tsx +445 -0
- package/recipes/0/apps/native/src/components/advanced-ui/timeline/timeline-view.tsx +355 -0
- package/recipes/0/apps/native/src/components/advanced-ui/timeline/types.ts +31 -0
- package/recipes/0/recipe.json +18 -0
- package/recipes/animated-chip/apps/native/src/components/advanced-ui/chip/demo.tsx +2 -1
- package/recipes/animated-chip/recipe.json +5 -2
- package/recipes/animated-chip-native@latest.zip +0 -0
- package/recipes/animated-chip@latest.zip +0 -0
- package/recipes/animated-switch/apps/native/src/components/advanced-ui/switch/demo.tsx +1 -1
- package/recipes/animated-switch/recipe.json +5 -2
- package/recipes/animated-switch-native@latest.zip +0 -0
- package/recipes/animated-switch@latest.zip +0 -0
- package/recipes/audio-recorder/apps/native/src/features/audio-recorder/components/audio-recorder.tsx +2 -1
- package/recipes/audio-recorder/apps/native/src/features/audio-recorder/demo/with-recording-list-demo.tsx +2 -2
- package/recipes/audio-recorder/recipe.json +7 -2
- package/recipes/audio-recorder-native@latest.zip +0 -0
- package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/audio-recorder.tsx +2 -1
- package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/with-recording-list-demo.tsx +2 -1
- package/recipes/audio-recorder-supabase/recipe.json +12 -16
- package/recipes/audio-recorder-supabase-native@latest.zip +0 -0
- package/recipes/audio-recorder-supabase@latest.zip +0 -0
- package/recipes/audio-recorder@latest.zip +0 -0
- package/recipes/charts/apps/native/src/app/charts/index.tsx +3 -0
- package/recipes/charts/apps/native/src/features/charts/components/bar-chart.tsx +3 -1
- package/recipes/charts/apps/native/src/features/charts/components/candlestick-chart.tsx +3 -1
- package/recipes/charts/apps/native/src/features/charts/components/column-chart.tsx +3 -1
- package/recipes/charts/apps/native/src/features/charts/components/doughnut-chart.tsx +3 -1
- package/recipes/charts/apps/native/src/features/charts/components/line-chart.tsx +3 -1
- package/recipes/charts/apps/native/src/features/charts/components/radar-chart.tsx +3 -1
- package/recipes/charts/apps/native/src/features/charts/components/stacked-bar-chart.tsx +3 -1
- package/recipes/charts/recipe.json +13 -4
- package/recipes/charts-native@latest.zip +0 -0
- package/recipes/charts@latest.zip +0 -0
- package/recipes/chatbot/apps/native/src/features/chatbot/components/chat-markdown.tsx +86 -86
- package/recipes/chatbot/apps/native/src/features/chatbot/components/markdown/code-block.tsx +4 -4
- package/recipes/chatbot/recipe.json +3 -40
- package/recipes/chatbot-native@latest.zip +0 -0
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-markdown.tsx +4 -1
- package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/markdown/code-block.tsx +86 -53
- package/recipes/chatbot-supabase/recipe.json +3 -42
- package/recipes/chatbot-supabase-native@latest.zip +0 -0
- package/recipes/chatbot-supabase@latest.zip +0 -0
- package/recipes/chatbot@latest.zip +0 -0
- package/recipes/glowing-button/recipe.json +6 -2
- package/recipes/glowing-button-native@latest.zip +0 -0
- package/recipes/glowing-button@latest.zip +0 -0
- package/recipes/image-analysis/apps/native/src/app/analysis/[type]/_layout.tsx +5 -0
- package/recipes/image-analysis/apps/native/src/app/analysis/[type]/analysis-options.tsx +50 -0
- package/recipes/image-analysis/apps/native/src/app/analysis/[type]/camera.tsx +2 -0
- package/recipes/image-analysis/apps/native/src/app/analysis/[type]/index.tsx +50 -0
- package/recipes/image-analysis/apps/native/src/app/analysis/[type]/loading.tsx +50 -0
- package/recipes/image-analysis/apps/native/src/app/analysis/[type]/results.tsx +2 -0
- package/recipes/image-analysis/apps/native/src/app/analysis/[type]/trait-details.tsx +3 -0
- package/recipes/image-analysis/apps/native/src/features/image-analyzer/app/analysis-options-screen.tsx +2 -2
- package/recipes/image-analysis/apps/native/src/features/image-analyzer/app/camera.tsx +72 -65
- package/recipes/image-analysis/apps/native/src/features/image-analyzer/app/image-capture-screen.tsx +65 -47
- package/recipes/image-analysis/apps/native/src/features/image-analyzer/app/loading-screen.tsx +43 -2
- package/recipes/image-analysis/apps/native/src/features/image-analyzer/app/loading.tsx +34 -1
- package/recipes/image-analysis/apps/native/src/features/image-analyzer/hooks/use-image-analysis.ts +83 -2
- package/recipes/image-analysis/recipe.json +11 -19
- package/recipes/image-analysis-native@latest.zip +0 -0
- package/recipes/image-analysis-supabase/apps/native/src/app/analysis/[type]/_layout.tsx +5 -0
- package/recipes/image-analysis-supabase/apps/native/src/app/analysis/[type]/analysis-options.tsx +50 -0
- package/recipes/image-analysis-supabase/apps/native/src/app/analysis/[type]/camera.tsx +2 -0
- package/recipes/image-analysis-supabase/apps/native/src/app/analysis/[type]/index.tsx +50 -0
- package/recipes/image-analysis-supabase/apps/native/src/app/analysis/[type]/loading.tsx +50 -0
- package/recipes/image-analysis-supabase/apps/native/src/app/analysis/[type]/results.tsx +2 -0
- package/recipes/image-analysis-supabase/apps/native/src/app/analysis/[type]/trait-details.tsx +3 -0
- package/recipes/image-analysis-supabase/recipe.json +10 -37
- package/recipes/image-analysis-supabase-native@latest.zip +0 -0
- package/recipes/image-analysis-supabase@latest.zip +0 -0
- package/recipes/image-analysis@latest.zip +0 -0
- package/recipes/image-analyzer/apps/native/src/app/(root)/(protected)/image-analyzer/index.tsx +2 -0
- package/recipes/image-generator/apps/native/src/app/image-generator/gallery.tsx +3 -0
- package/recipes/image-generator/apps/native/src/app/image-generator/index.tsx +3 -0
- package/recipes/image-generator/recipe.json +8 -18
- package/recipes/image-generator-native@latest.zip +0 -0
- package/recipes/image-generator-supabase/recipe.json +6 -35
- package/recipes/image-generator-supabase-native@latest.zip +0 -0
- package/recipes/image-generator-supabase@latest.zip +0 -0
- package/recipes/image-generator@latest.zip +0 -0
- package/recipes/ios-widget/recipe.json +18 -119
- package/recipes/ios-widget-native@latest.zip +0 -0
- package/recipes/ios-widget@latest.zip +0 -0
- package/recipes/number-stepper/apps/native/src/components/advanced-ui/stepper/demo.tsx +1 -1
- package/recipes/number-stepper/recipe.json +5 -2
- package/recipes/number-stepper-native@latest.zip +0 -0
- package/recipes/number-stepper@latest.zip +0 -0
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/interactive-onboarding.tsx +11 -18
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/ai-tone-step.tsx +5 -7
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/currency-step.tsx +9 -7
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-ai-step.tsx +8 -7
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-chatbot-step.tsx +6 -5
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-manual-step.tsx +4 -3
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-scan-step.tsx +6 -5
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/main-reason-step.tsx +5 -7
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/notification-step.tsx +7 -6
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/overspend-step.tsx +5 -7
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/personalizing-step.tsx +8 -7
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/rating-step.tsx +6 -5
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/reminder-step.tsx +5 -6
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/safety-step.tsx +5 -4
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/struggle-step.tsx +5 -7
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/welcome-step.tsx +7 -6
- package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/ui/onboarding-header.tsx +4 -3
- package/recipes/onboarding/recipe.json +9 -6
- package/recipes/onboarding-native@latest.zip +0 -0
- package/recipes/onboarding@latest.zip +0 -0
- package/recipes/payments/apps/native/src/app/paywall/index.tsx +74 -0
- package/recipes/payments/apps/native/src/app/paywall/local.tsx +25 -0
- package/recipes/payments/apps/native/src/app/paywall/remote.tsx +23 -0
- package/recipes/payments/packages/backend/convex/payments.ts +21 -3
- package/recipes/payments/recipe.json +14 -34
- package/recipes/payments-native@latest.zip +0 -0
- package/recipes/payments-supabase/apps/native/src/app/paywall/index.tsx +74 -0
- package/recipes/payments-supabase/apps/native/src/app/paywall/local.tsx +25 -0
- package/recipes/payments-supabase/apps/native/src/app/paywall/remote.tsx +23 -0
- package/recipes/payments-supabase/recipe.json +16 -23
- package/recipes/payments-supabase-native@latest.zip +0 -0
- package/recipes/payments-supabase@latest.zip +0 -0
- package/recipes/payments@latest.zip +0 -0
- package/recipes/posthog/apps/native/src/components/analytics/navigation-tracker.tsx +14 -0
- package/recipes/posthog/apps/native/src/lib/hooks/use-navigation-analytics.ts +44 -0
- package/recipes/posthog/apps/native/src/providers/posthog-provider.tsx +51 -0
- package/recipes/posthog/recipe.json +60 -0
- package/recipes/posthog-native@latest.zip +0 -0
- package/recipes/progress-circle/apps/native/src/components/advanced-ui/progress-bars/progress-circle-page.tsx +1 -1
- package/recipes/progress-circle/recipe.json +5 -2
- package/recipes/progress-circle-native@latest.zip +0 -0
- package/recipes/progress-circle@latest.zip +0 -0
- package/recipes/quiz/apps/native/src/app/quiz/index.tsx +47 -0
- package/recipes/quiz/recipe.json +9 -6
- package/recipes/quiz-native@latest.zip +0 -0
- package/recipes/quiz@latest.zip +0 -0
- package/recipes/screen-kits/apps/native/src/app/screen-kits/_layout.tsx +12 -0
- package/recipes/screen-kits/apps/native/src/app/screen-kits/index.tsx +114 -0
- package/recipes/screen-kits/apps/native/src/features/screen-kits/index.ts +1 -0
- package/recipes/screen-kits/apps/native/src/features/screen-kits/types.ts +28 -0
- package/recipes/screen-kits/recipe.json +26 -0
- package/recipes/screen-kits-duolingo/apps/native/src/app/screen-kits/duolingo/_layout.tsx +12 -0
- package/recipes/screen-kits-duolingo/apps/native/src/app/screen-kits/duolingo/home.tsx +5 -0
- package/recipes/screen-kits-duolingo/apps/native/src/app/screen-kits/duolingo/index.tsx +5 -0
- package/recipes/screen-kits-duolingo/apps/native/src/app/screen-kits/duolingo/lesson-complete.tsx +5 -0
- package/recipes/screen-kits-duolingo/apps/native/src/app/screen-kits/duolingo/lesson-fail.tsx +5 -0
- package/recipes/screen-kits-duolingo/apps/native/src/app/screen-kits/duolingo/lesson.tsx +5 -0
- package/recipes/screen-kits-duolingo/apps/native/src/app/screen-kits/duolingo/skill-tree.tsx +5 -0
- package/recipes/screen-kits-duolingo/apps/native/src/features/screen-kits/duolingo/components/duo-button.tsx +174 -0
- package/recipes/screen-kits-duolingo/apps/native/src/features/screen-kits/duolingo/components/skill-button.tsx +186 -0
- package/recipes/screen-kits-duolingo/apps/native/src/features/screen-kits/duolingo/components/xp-header.tsx +115 -0
- package/recipes/screen-kits-duolingo/apps/native/src/features/screen-kits/duolingo/constants.ts +89 -0
- package/recipes/screen-kits-duolingo/apps/native/src/features/screen-kits/duolingo/index.ts +3 -0
- package/recipes/screen-kits-duolingo/apps/native/src/features/screen-kits/duolingo/screens/home-screen.tsx +225 -0
- package/recipes/screen-kits-duolingo/apps/native/src/features/screen-kits/duolingo/screens/lesson-complete-screen.tsx +485 -0
- package/recipes/screen-kits-duolingo/apps/native/src/features/screen-kits/duolingo/screens/lesson-fail-screen.tsx +105 -0
- package/recipes/screen-kits-duolingo/apps/native/src/features/screen-kits/duolingo/screens/lesson-screen.tsx +384 -0
- package/recipes/screen-kits-duolingo/recipe.json +58 -0
- package/recipes/screen-kits-duolingo-native@latest.zip +0 -0
- package/recipes/screen-kits-finance/apps/native/src/app/screen-kits/finance/_layout.tsx +45 -0
- package/recipes/screen-kits-finance/apps/native/src/app/screen-kits/finance/asset-detail.tsx +3 -0
- package/recipes/screen-kits-finance/apps/native/src/app/screen-kits/finance/notifications.tsx +3 -0
- package/recipes/screen-kits-finance/apps/native/src/app/screen-kits/finance/receive.tsx +3 -0
- package/recipes/screen-kits-finance/apps/native/src/app/screen-kits/finance/send.tsx +3 -0
- package/recipes/screen-kits-finance/apps/native/src/app/screen-kits/finance/swap.tsx +3 -0
- package/recipes/screen-kits-finance/apps/native/src/app/screen-kits/finance/wallet.tsx +3 -0
- package/recipes/screen-kits-finance/apps/native/src/features/screen-kits/finance/components/ActionButtons.tsx +78 -0
- package/recipes/screen-kits-finance/apps/native/src/features/screen-kits/finance/components/AssetRow.tsx +94 -0
- package/recipes/screen-kits-finance/apps/native/src/features/screen-kits/finance/components/BalanceCard.tsx +118 -0
- package/recipes/screen-kits-finance/apps/native/src/features/screen-kits/finance/constants.ts +85 -0
- package/recipes/screen-kits-finance/apps/native/src/features/screen-kits/finance/screens/asset-detail.tsx +378 -0
- package/recipes/screen-kits-finance/apps/native/src/features/screen-kits/finance/screens/notifications.tsx +210 -0
- package/recipes/screen-kits-finance/apps/native/src/features/screen-kits/finance/screens/receive-modal.tsx +317 -0
- package/recipes/screen-kits-finance/apps/native/src/features/screen-kits/finance/screens/send-modal.tsx +420 -0
- package/recipes/screen-kits-finance/apps/native/src/features/screen-kits/finance/screens/swap-modal.tsx +363 -0
- package/recipes/screen-kits-finance/apps/native/src/features/screen-kits/finance/screens/wallet-dashboard.tsx +281 -0
- package/recipes/screen-kits-finance/recipe.json +46 -0
- package/recipes/screen-kits-finance-native@latest.zip +0 -0
- package/recipes/screen-kits-fitness/apps/native/assets/sounds/timer-beep.wav +0 -0
- package/recipes/screen-kits-fitness/apps/native/src/app/screen-kits/fitness/_layout.tsx +10 -0
- package/recipes/screen-kits-fitness/apps/native/src/app/screen-kits/fitness/index.tsx +6 -0
- package/recipes/screen-kits-fitness/apps/native/src/app/screen-kits/fitness/timer.tsx +3 -0
- package/recipes/screen-kits-fitness/apps/native/src/app/screen-kits/fitness/workout.tsx +3 -0
- package/recipes/screen-kits-fitness/apps/native/src/features/screen-kits/fitness/components/timer-components.tsx +500 -0
- package/recipes/screen-kits-fitness/apps/native/src/features/screen-kits/fitness/components/timer-settings-modal.tsx +352 -0
- package/recipes/screen-kits-fitness/apps/native/src/features/screen-kits/fitness/components/workout-card.tsx +105 -0
- package/recipes/screen-kits-fitness/apps/native/src/features/screen-kits/fitness/constants.ts +189 -0
- package/recipes/screen-kits-fitness/apps/native/src/features/screen-kits/fitness/hooks/use-timer.ts +307 -0
- package/recipes/screen-kits-fitness/apps/native/src/features/screen-kits/fitness/index.ts +1 -0
- package/recipes/screen-kits-fitness/apps/native/src/features/screen-kits/fitness/screens/timer-screen.tsx +278 -0
- package/recipes/screen-kits-fitness/apps/native/src/features/screen-kits/fitness/screens/workout-dashboard.tsx +350 -0
- package/recipes/screen-kits-fitness/recipe.json +63 -0
- package/recipes/screen-kits-fitness-native@latest.zip +0 -0
- package/recipes/screen-kits-habits/apps/native/src/app/screen-kits/productivity/habits.tsx +1 -0
- package/recipes/screen-kits-habits/apps/native/src/app/screen-kits/productivity/kanban.tsx +1 -0
- package/recipes/screen-kits-habits/apps/native/src/app/screen-kits/productivity/routes.ts +4 -0
- package/recipes/screen-kits-habits/apps/native/src/features/screen-kits/productivity/components/AddTaskModal.tsx +246 -0
- package/recipes/screen-kits-habits/apps/native/src/features/screen-kits/productivity/components/DraggableTaskCard.tsx +92 -0
- package/recipes/screen-kits-habits/apps/native/src/features/screen-kits/productivity/components/KanbanColumn.tsx +238 -0
- package/recipes/screen-kits-habits/apps/native/src/features/screen-kits/productivity/components/TaskCard.tsx +144 -0
- package/recipes/screen-kits-habits/apps/native/src/features/screen-kits/productivity/components/add-habit-modal.tsx +271 -0
- package/recipes/screen-kits-habits/apps/native/src/features/screen-kits/productivity/constants.ts +295 -0
- package/recipes/screen-kits-habits/apps/native/src/features/screen-kits/productivity/kanban-utils.ts +62 -0
- package/recipes/screen-kits-habits/apps/native/src/features/screen-kits/productivity/screens/habit-tracker.tsx +1160 -0
- package/recipes/screen-kits-habits/apps/native/src/features/screen-kits/productivity/screens/kanban-board.tsx +432 -0
- package/recipes/screen-kits-habits/recipe.json +52 -0
- package/recipes/screen-kits-habits-native@latest.zip +0 -0
- package/recipes/screen-kits-native@latest.zip +0 -0
- package/recipes/sentry/apps/native/src/providers/sentry-provider.tsx +64 -0
- package/recipes/sentry/recipe.json +39 -0
- package/recipes/sentry-native@latest.zip +0 -0
- package/recipes/swipe-slider/apps/native/src/components/advanced-ui/sliders/swipe-slider-page.tsx +1 -1
- package/recipes/swipe-slider/recipe.json +5 -2
- package/recipes/swipe-slider-native@latest.zip +0 -0
- package/recipes/swipe-slider@latest.zip +0 -0
- package/recipes/timeline/apps/native/src/components/advanced-ui/timeline/demo.tsx +2 -1
- package/recipes/timeline/recipe.json +5 -2
- package/recipes/timeline-native@latest.zip +0 -0
- package/recipes/timeline@latest.zip +0 -0
- package/recipes/tracker-app/apps/native/src/app/tracker-app/index.tsx +1 -0
- package/recipes/tracker-app/recipe.json +10 -7
- package/recipes/tracker-app-native@latest.zip +0 -0
- package/recipes/tracker-app@latest.zip +0 -0
- package/recipes/upload-all.sh +8 -31
- package/recipes/voice-bot/apps/native/src/app/voice-bot/index.tsx +56 -0
- package/recipes/voice-bot/recipe.json +31 -7
- package/recipes/voice-bot-native@latest.zip +0 -0
- package/recipes/voice-bot@latest.zip +0 -0
- package/recipes/wake-word/apps/native/src/app/{(root)/(protected)/test-wake-word.tsx → test-wake-word.tsx} +43 -4
- package/recipes/wake-word/recipe.json +16 -26
- package/recipes/wake-word-native@latest.zip +0 -0
- package/recipes/wake-word@latest.zip +0 -0
- package/scripts/create-advanced-ui-recipes.sh +46 -19
- package/scripts/create-recipes.mjs +471 -117
- package/scripts/package-recipes.mjs +76 -0
- package/scripts/publish-all.sh +6 -2
- package/CHANGELOG.md +0 -198
- package/docs/archive/AUTO-DETECT-DEPS.md +0 -607
- package/docs/archive/FINAL-PACKAGE-STRATEGY.md +0 -583
- package/docs/archive/FINAL-SIMPLE-PLAN.md +0 -487
- package/docs/archive/FINAL-STATUS.md +0 -144
- package/docs/archive/FLOW-DIAGRAM.md +0 -1629
- package/docs/archive/GOTCHAS-AND-RISKS.md +0 -801
- package/docs/archive/IMPLEMENTATION-PLAN.md +0 -1360
- package/docs/archive/PLAN.md +0 -453
- package/docs/archive/PRODUCTION-READINESS.md +0 -684
- package/docs/archive/PRODUCTION-TEST-RESULTS.md +0 -465
- package/docs/archive/SIMPLIFIED-PLAN.md +0 -578
- package/docs/archive/STATUS.md +0 -199
- package/docs/archive/SUCCESS.md +0 -259
- package/docs/archive/TEST-SUMMARY.md +0 -261
- package/docs/archive/TESTING-CHECKLIST.md +0 -450
- package/docs/archive/USER-MODIFICATIONS.md +0 -448
- package/docs/decisions.md +0 -55
- package/docs/manual-testing.md +0 -91
- package/docs/next-steps.md +0 -12
- package/recipes/README.md +0 -156
- package/recipes/audio-recorder-supabase/packages/backend/src/services/recordings.ts +0 -369
- package/recipes/chatbot/apps/native/src/api-client/chatbot.ts +0 -83
- package/recipes/chatbot/packages/backend/convex/agents.ts +0 -115
- package/recipes/chatbot/packages/backend/convex/tools/index.ts +0 -18
- package/recipes/chatbot/packages/backend/convex/tools/knowledgeRetrieval.ts +0 -97
- package/recipes/chatbot/packages/backend/convex/tools/tavilySearch.ts +0 -83
- package/recipes/chatbot/packages/backend/convex/tools/userProfile.ts +0 -72
- package/recipes/chatbot-supabase/apps/native/src/api-client/supabase/chatbot.ts +0 -515
- package/recipes/chatbot-supabase/packages/backend/src/services/conversations.ts +0 -243
- package/recipes/chatbot-supabase/packages/backend/src/services/messages.ts +0 -327
- package/recipes/image-analysis/apps/native/src/api-client/image-analyzer.ts +0 -62
- package/recipes/image-analysis-supabase/packages/backend/src/services/image-analyses.ts +0 -132
- package/recipes/image-generator/apps/native/src/api-client/image-generator.ts +0 -34
- package/recipes/payments/apps/native/src/api-client/payments.ts +0 -44
- package/recipes/payments-supabase/packages/backend/src/services/payments.ts +0 -201
- package/recipes/posthog.json +0 -47
- package/recipes/revenuecat.json +0 -43
- package/recipes/sentry.json +0 -47
- package/recipes/wake-word/apps/native/assets/vosk-model/README.md +0 -103
- package/recipes/wake-word/apps/native/scripts/download-vosk-model.mjs +0 -127
- /package/recipes/{audio-recorder/apps/native/src/app/(root)/(protected) → audio-recorder-supabase/apps/native/src/app}/audio-recorder/index.tsx +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/AppIntent.swift +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-20x20@1x.png +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-20x20@2x.png +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-20x20@3x.png +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-29x29@1x.png +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-29x29@2x.png +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-29x29@3x.png +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-40x40@1x.png +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-40x40@2x.png +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-40x40@3x.png +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-60x60@2x.png +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-60x60@3x.png +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-76x76@1x.png +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-76x76@2x.png +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-83.5x83.5@2x.png +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/CalorieTrackerWidget.swift +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/HabitTrackerWidget.swift +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Info.plist +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/WidgetLiveActivity.swift +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/expo-target.config.js +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/generated.entitlements +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/index.swift +0 -0
- /package/recipes/ios-widget/{targets → apps/native/targets}/widget/widgets.swift +0 -0
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Conversations Service
|
|
3
|
-
*
|
|
4
|
-
* Handles conversation operations including getOrCreateDefault, create, list, and delete.
|
|
5
|
-
*
|
|
6
|
-
* Requirements: 4.1, 4.2
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { SupabaseClientType } from '../lib/supabase';
|
|
10
|
-
import type { Conversation } from '../types';
|
|
11
|
-
|
|
12
|
-
const DEFAULT_CONVERSATION_NAME = 'Default Chat';
|
|
13
|
-
|
|
14
|
-
export interface GetConversationResult {
|
|
15
|
-
success: boolean;
|
|
16
|
-
conversation?: Conversation;
|
|
17
|
-
error?: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface ListConversationsResult {
|
|
21
|
-
success: boolean;
|
|
22
|
-
conversations?: Conversation[];
|
|
23
|
-
error?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface DeleteConversationResult {
|
|
27
|
-
success: boolean;
|
|
28
|
-
error?: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Get or create the default conversation for the current user
|
|
33
|
-
*
|
|
34
|
-
* This function returns an existing conversation named "Default Chat" or creates
|
|
35
|
-
* a new one if it doesn't exist. This ensures idempotency - calling multiple times
|
|
36
|
-
* always returns the same conversation.
|
|
37
|
-
*
|
|
38
|
-
* @param supabase - Supabase client (authenticated)
|
|
39
|
-
* @returns The default conversation
|
|
40
|
-
*
|
|
41
|
-
* Requirements: 4.1
|
|
42
|
-
*/
|
|
43
|
-
export async function getOrCreateDefault(
|
|
44
|
-
supabase: SupabaseClientType
|
|
45
|
-
): Promise<GetConversationResult> {
|
|
46
|
-
// Get the current auth user
|
|
47
|
-
const { data: authData, error: authError } = await supabase.auth.getUser();
|
|
48
|
-
|
|
49
|
-
if (authError || !authData.user) {
|
|
50
|
-
return { success: false, error: 'Not authenticated' };
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const userId = authData.user.id;
|
|
54
|
-
|
|
55
|
-
// Try to find existing default conversation (use limit 1 to handle duplicates)
|
|
56
|
-
const { data: existingList, error: findError } = await supabase
|
|
57
|
-
.from('conversations')
|
|
58
|
-
.select('*')
|
|
59
|
-
.eq('user_id', userId)
|
|
60
|
-
.eq('name', DEFAULT_CONVERSATION_NAME)
|
|
61
|
-
.order('created_at', { ascending: true })
|
|
62
|
-
.limit(1);
|
|
63
|
-
|
|
64
|
-
if (findError) {
|
|
65
|
-
return { success: false, error: `Failed to find conversation: ${findError.message}` };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Return existing if found
|
|
69
|
-
if (existingList && existingList.length > 0) {
|
|
70
|
-
return { success: true, conversation: existingList[0] };
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Create new default conversation
|
|
74
|
-
const { data: created, error: createError } = await supabase
|
|
75
|
-
.from('conversations')
|
|
76
|
-
.insert({
|
|
77
|
-
user_id: userId,
|
|
78
|
-
name: DEFAULT_CONVERSATION_NAME,
|
|
79
|
-
})
|
|
80
|
-
.select()
|
|
81
|
-
.single();
|
|
82
|
-
|
|
83
|
-
if (createError) {
|
|
84
|
-
return { success: false, error: `Failed to create conversation: ${createError.message}` };
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return { success: true, conversation: created };
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Create a new conversation
|
|
93
|
-
*
|
|
94
|
-
* @param supabase - Supabase client (authenticated)
|
|
95
|
-
* @param name - Optional name for the conversation
|
|
96
|
-
* @returns The created conversation
|
|
97
|
-
*
|
|
98
|
-
* Requirements: 4.2
|
|
99
|
-
*/
|
|
100
|
-
export async function create(
|
|
101
|
-
supabase: SupabaseClientType,
|
|
102
|
-
name?: string
|
|
103
|
-
): Promise<GetConversationResult> {
|
|
104
|
-
// Get the current auth user
|
|
105
|
-
const { data: authData, error: authError } = await supabase.auth.getUser();
|
|
106
|
-
|
|
107
|
-
if (authError || !authData.user) {
|
|
108
|
-
return { success: false, error: 'Not authenticated' };
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const { data: conversation, error: createError } = await supabase
|
|
112
|
-
.from('conversations')
|
|
113
|
-
.insert({
|
|
114
|
-
user_id: authData.user.id,
|
|
115
|
-
name: name || null,
|
|
116
|
-
})
|
|
117
|
-
.select()
|
|
118
|
-
.single();
|
|
119
|
-
|
|
120
|
-
if (createError) {
|
|
121
|
-
return { success: false, error: `Failed to create conversation: ${createError.message}` };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return { success: true, conversation };
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* List all conversations for the current user
|
|
129
|
-
*
|
|
130
|
-
* @param supabase - Supabase client (authenticated)
|
|
131
|
-
* @returns List of conversations ordered by creation time (newest first)
|
|
132
|
-
*
|
|
133
|
-
* Requirements: 4.2
|
|
134
|
-
*/
|
|
135
|
-
export async function list(
|
|
136
|
-
supabase: SupabaseClientType
|
|
137
|
-
): Promise<ListConversationsResult> {
|
|
138
|
-
// Get the current auth user
|
|
139
|
-
const { data: authData, error: authError } = await supabase.auth.getUser();
|
|
140
|
-
|
|
141
|
-
if (authError || !authData.user) {
|
|
142
|
-
return { success: false, error: 'Not authenticated' };
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const { data: conversations, error: listError } = await supabase
|
|
146
|
-
.from('conversations')
|
|
147
|
-
.select('*')
|
|
148
|
-
.eq('user_id', authData.user.id)
|
|
149
|
-
.order('created_at', { ascending: false });
|
|
150
|
-
|
|
151
|
-
if (listError) {
|
|
152
|
-
return { success: false, error: `Failed to list conversations: ${listError.message}` };
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return { success: true, conversations: conversations || [] };
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Delete a conversation
|
|
160
|
-
*
|
|
161
|
-
* This will also delete all messages in the conversation due to CASCADE.
|
|
162
|
-
*
|
|
163
|
-
* @param supabase - Supabase client (authenticated)
|
|
164
|
-
* @param conversationId - The ID of the conversation to delete
|
|
165
|
-
* @returns Success or error
|
|
166
|
-
*
|
|
167
|
-
* Requirements: 4.2
|
|
168
|
-
*/
|
|
169
|
-
export async function deleteConversation(
|
|
170
|
-
supabase: SupabaseClientType,
|
|
171
|
-
conversationId: string
|
|
172
|
-
): Promise<DeleteConversationResult> {
|
|
173
|
-
// Get the current auth user
|
|
174
|
-
const { data: authData, error: authError } = await supabase.auth.getUser();
|
|
175
|
-
|
|
176
|
-
if (authError || !authData.user) {
|
|
177
|
-
return { success: false, error: 'Not authenticated' };
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (!conversationId) {
|
|
181
|
-
return { success: false, error: 'Conversation ID is required' };
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Delete the conversation (RLS ensures user can only delete their own)
|
|
185
|
-
const { error: deleteError } = await supabase
|
|
186
|
-
.from('conversations')
|
|
187
|
-
.delete()
|
|
188
|
-
.eq('id', conversationId)
|
|
189
|
-
.eq('user_id', authData.user.id);
|
|
190
|
-
|
|
191
|
-
if (deleteError) {
|
|
192
|
-
return { success: false, error: `Failed to delete conversation: ${deleteError.message}` };
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return { success: true };
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Get a conversation by ID
|
|
200
|
-
*
|
|
201
|
-
* @param supabase - Supabase client (authenticated)
|
|
202
|
-
* @param conversationId - The ID of the conversation
|
|
203
|
-
* @returns The conversation or error
|
|
204
|
-
*/
|
|
205
|
-
export async function getById(
|
|
206
|
-
supabase: SupabaseClientType,
|
|
207
|
-
conversationId: string
|
|
208
|
-
): Promise<GetConversationResult> {
|
|
209
|
-
// Get the current auth user
|
|
210
|
-
const { data: authData, error: authError } = await supabase.auth.getUser();
|
|
211
|
-
|
|
212
|
-
if (authError || !authData.user) {
|
|
213
|
-
return { success: false, error: 'Not authenticated' };
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (!conversationId) {
|
|
217
|
-
return { success: false, error: 'Conversation ID is required' };
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const { data: conversation, error: getError } = await supabase
|
|
221
|
-
.from('conversations')
|
|
222
|
-
.select('*')
|
|
223
|
-
.eq('id', conversationId)
|
|
224
|
-
.eq('user_id', authData.user.id)
|
|
225
|
-
.single();
|
|
226
|
-
|
|
227
|
-
if (getError) {
|
|
228
|
-
if (getError.code === 'PGRST116') {
|
|
229
|
-
return { success: false, error: 'Conversation not found' };
|
|
230
|
-
}
|
|
231
|
-
return { success: false, error: `Failed to get conversation: ${getError.message}` };
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return { success: true, conversation };
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
export const conversationsService = {
|
|
238
|
-
getOrCreateDefault,
|
|
239
|
-
create,
|
|
240
|
-
list,
|
|
241
|
-
delete: deleteConversation,
|
|
242
|
-
getById,
|
|
243
|
-
};
|
|
@@ -1,327 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Messages Service
|
|
3
|
-
*
|
|
4
|
-
* Handles message operations including storeMessage, listMessages,
|
|
5
|
-
* deleteUserMessage, and clearConversationMessages.
|
|
6
|
-
*
|
|
7
|
-
* Requirements: 4.3, 4.4, 4.5, 4.6, 4.7
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import type { SupabaseClientType } from '../lib/supabase';
|
|
11
|
-
import type { Message } from '../types';
|
|
12
|
-
import type { Json } from '../types/database';
|
|
13
|
-
|
|
14
|
-
export interface StoreMessageInput {
|
|
15
|
-
conversationId: string;
|
|
16
|
-
authorType: 'user' | 'bot';
|
|
17
|
-
text?: string | null;
|
|
18
|
-
attachments?: Json | null;
|
|
19
|
-
aiProvider?: string | null;
|
|
20
|
-
aiModel?: string | null;
|
|
21
|
-
toolCalls?: Json | null;
|
|
22
|
-
metadata?: Json | null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface StoreMessageResult {
|
|
26
|
-
success: boolean;
|
|
27
|
-
message?: Message;
|
|
28
|
-
error?: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface ListMessagesResult {
|
|
32
|
-
success: boolean;
|
|
33
|
-
messages?: Message[];
|
|
34
|
-
hasMore?: boolean;
|
|
35
|
-
error?: string;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export interface DeleteMessageResult {
|
|
39
|
-
success: boolean;
|
|
40
|
-
error?: string;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface ClearMessagesResult {
|
|
44
|
-
success: boolean;
|
|
45
|
-
deletedCount?: number;
|
|
46
|
-
error?: string;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const DEFAULT_PAGE_SIZE = 50;
|
|
50
|
-
const BATCH_DELETE_SIZE = 100;
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Store a new message in a conversation
|
|
54
|
-
*
|
|
55
|
-
* Validates that the message has either text or attachments (not both empty).
|
|
56
|
-
*
|
|
57
|
-
* @param supabase - Supabase client (authenticated)
|
|
58
|
-
* @param input - Message data to store
|
|
59
|
-
* @returns The created message
|
|
60
|
-
*
|
|
61
|
-
* Requirements: 4.3, 4.4
|
|
62
|
-
*/
|
|
63
|
-
export async function storeMessage(
|
|
64
|
-
supabase: SupabaseClientType,
|
|
65
|
-
input: StoreMessageInput
|
|
66
|
-
): Promise<StoreMessageResult> {
|
|
67
|
-
// Get the current auth user
|
|
68
|
-
const { data: authData, error: authError } = await supabase.auth.getUser();
|
|
69
|
-
|
|
70
|
-
if (authError || !authData.user) {
|
|
71
|
-
return { success: false, error: 'Not authenticated' };
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const userId = authData.user.id;
|
|
75
|
-
|
|
76
|
-
// Validate input
|
|
77
|
-
if (!input.conversationId) {
|
|
78
|
-
return { success: false, error: 'Conversation ID is required' };
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Check if text is empty/whitespace and no attachments
|
|
82
|
-
const hasText = input.text && input.text.trim().length > 0;
|
|
83
|
-
const hasAttachments = input.attachments &&
|
|
84
|
-
(Array.isArray(input.attachments) ? input.attachments.length > 0 : true);
|
|
85
|
-
|
|
86
|
-
if (!hasText && !hasAttachments) {
|
|
87
|
-
return { success: false, error: 'Message must have text or attachments' };
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Verify user owns the conversation
|
|
91
|
-
const { data: conversation, error: convError } = await supabase
|
|
92
|
-
.from('conversations')
|
|
93
|
-
.select('id')
|
|
94
|
-
.eq('id', input.conversationId)
|
|
95
|
-
.eq('user_id', userId)
|
|
96
|
-
.single();
|
|
97
|
-
|
|
98
|
-
if (convError || !conversation) {
|
|
99
|
-
return { success: false, error: 'Conversation not found or access denied' };
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Create the message
|
|
103
|
-
const { data: message, error: createError } = await supabase
|
|
104
|
-
.from('messages')
|
|
105
|
-
.insert({
|
|
106
|
-
conversation_id: input.conversationId,
|
|
107
|
-
user_id: userId,
|
|
108
|
-
author_type: input.authorType,
|
|
109
|
-
text: input.text || null,
|
|
110
|
-
attachments: input.attachments || null,
|
|
111
|
-
ai_provider: input.aiProvider || null,
|
|
112
|
-
ai_model: input.aiModel || null,
|
|
113
|
-
tool_calls: input.toolCalls || null,
|
|
114
|
-
metadata: input.metadata || null,
|
|
115
|
-
})
|
|
116
|
-
.select()
|
|
117
|
-
.single();
|
|
118
|
-
|
|
119
|
-
if (createError) {
|
|
120
|
-
return { success: false, error: `Failed to store message: ${createError.message}` };
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return { success: true, message };
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* List messages for a conversation with pagination
|
|
129
|
-
*
|
|
130
|
-
* Returns messages ordered by creation time (oldest first for chat display).
|
|
131
|
-
*
|
|
132
|
-
* @param supabase - Supabase client (authenticated)
|
|
133
|
-
* @param conversationId - The conversation to list messages from
|
|
134
|
-
* @param options - Pagination options
|
|
135
|
-
* @returns Paginated list of messages
|
|
136
|
-
*
|
|
137
|
-
* Requirements: 4.5
|
|
138
|
-
*/
|
|
139
|
-
export async function listMessages(
|
|
140
|
-
supabase: SupabaseClientType,
|
|
141
|
-
conversationId: string,
|
|
142
|
-
options?: {
|
|
143
|
-
limit?: number;
|
|
144
|
-
cursor?: string; // created_at timestamp for cursor-based pagination
|
|
145
|
-
}
|
|
146
|
-
): Promise<ListMessagesResult> {
|
|
147
|
-
// Get the current auth user
|
|
148
|
-
const { data: authData, error: authError } = await supabase.auth.getUser();
|
|
149
|
-
|
|
150
|
-
if (authError || !authData.user) {
|
|
151
|
-
return { success: false, error: 'Not authenticated' };
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (!conversationId) {
|
|
155
|
-
return { success: false, error: 'Conversation ID is required' };
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const limit = options?.limit || DEFAULT_PAGE_SIZE;
|
|
159
|
-
|
|
160
|
-
// Build query
|
|
161
|
-
let query = supabase
|
|
162
|
-
.from('messages')
|
|
163
|
-
.select('*')
|
|
164
|
-
.eq('conversation_id', conversationId)
|
|
165
|
-
.order('created_at', { ascending: true })
|
|
166
|
-
.limit(limit + 1); // Fetch one extra to check if there are more
|
|
167
|
-
|
|
168
|
-
// Apply cursor if provided
|
|
169
|
-
if (options?.cursor) {
|
|
170
|
-
query = query.gt('created_at', options.cursor);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const { data: messages, error: listError } = await query;
|
|
174
|
-
|
|
175
|
-
if (listError) {
|
|
176
|
-
return { success: false, error: `Failed to list messages: ${listError.message}` };
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Check if there are more results
|
|
180
|
-
const hasMore = messages && messages.length > limit;
|
|
181
|
-
const resultMessages = hasMore ? messages.slice(0, limit) : (messages || []);
|
|
182
|
-
|
|
183
|
-
return {
|
|
184
|
-
success: true,
|
|
185
|
-
messages: resultMessages,
|
|
186
|
-
hasMore,
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Delete a single user message
|
|
192
|
-
*
|
|
193
|
-
* Only allows deletion if the user owns the message.
|
|
194
|
-
*
|
|
195
|
-
* @param supabase - Supabase client (authenticated)
|
|
196
|
-
* @param messageId - The ID of the message to delete
|
|
197
|
-
* @returns Success or error
|
|
198
|
-
*
|
|
199
|
-
* Requirements: 4.7
|
|
200
|
-
*/
|
|
201
|
-
export async function deleteUserMessage(
|
|
202
|
-
supabase: SupabaseClientType,
|
|
203
|
-
messageId: string
|
|
204
|
-
): Promise<DeleteMessageResult> {
|
|
205
|
-
// Get the current auth user
|
|
206
|
-
const { data: authData, error: authError } = await supabase.auth.getUser();
|
|
207
|
-
|
|
208
|
-
if (authError || !authData.user) {
|
|
209
|
-
return { success: false, error: 'Not authenticated' };
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (!messageId) {
|
|
213
|
-
return { success: false, error: 'Message ID is required' };
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Delete the message (RLS + user_id check ensures ownership)
|
|
217
|
-
const { data, error: deleteError } = await supabase
|
|
218
|
-
.from('messages')
|
|
219
|
-
.delete()
|
|
220
|
-
.eq('id', messageId)
|
|
221
|
-
.eq('user_id', authData.user.id)
|
|
222
|
-
.select('id')
|
|
223
|
-
.single();
|
|
224
|
-
|
|
225
|
-
if (deleteError) {
|
|
226
|
-
if (deleteError.code === 'PGRST116') {
|
|
227
|
-
return { success: false, error: 'Message not found or access denied' };
|
|
228
|
-
}
|
|
229
|
-
return { success: false, error: `Failed to delete message: ${deleteError.message}` };
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (!data) {
|
|
233
|
-
return { success: false, error: 'Message not found or access denied' };
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return { success: true };
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Clear all messages in a conversation
|
|
241
|
-
*
|
|
242
|
-
* Deletes messages in batches to handle large conversations.
|
|
243
|
-
* The conversation itself is preserved.
|
|
244
|
-
*
|
|
245
|
-
* @param supabase - Supabase client (authenticated)
|
|
246
|
-
* @param conversationId - The conversation to clear
|
|
247
|
-
* @returns Success with count of deleted messages
|
|
248
|
-
*
|
|
249
|
-
* Requirements: 4.6
|
|
250
|
-
*/
|
|
251
|
-
export async function clearConversationMessages(
|
|
252
|
-
supabase: SupabaseClientType,
|
|
253
|
-
conversationId: string
|
|
254
|
-
): Promise<ClearMessagesResult> {
|
|
255
|
-
// Get the current auth user
|
|
256
|
-
const { data: authData, error: authError } = await supabase.auth.getUser();
|
|
257
|
-
|
|
258
|
-
if (authError || !authData.user) {
|
|
259
|
-
return { success: false, error: 'Not authenticated' };
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
if (!conversationId) {
|
|
263
|
-
return { success: false, error: 'Conversation ID is required' };
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
const userId = authData.user.id;
|
|
267
|
-
|
|
268
|
-
// Verify user owns the conversation
|
|
269
|
-
const { data: conversation, error: convError } = await supabase
|
|
270
|
-
.from('conversations')
|
|
271
|
-
.select('id')
|
|
272
|
-
.eq('id', conversationId)
|
|
273
|
-
.eq('user_id', userId)
|
|
274
|
-
.single();
|
|
275
|
-
|
|
276
|
-
if (convError || !conversation) {
|
|
277
|
-
return { success: false, error: 'Conversation not found or access denied' };
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
let totalDeleted = 0;
|
|
281
|
-
let hasMore = true;
|
|
282
|
-
|
|
283
|
-
// Delete in batches
|
|
284
|
-
while (hasMore) {
|
|
285
|
-
// Get batch of message IDs
|
|
286
|
-
const { data: messagesToDelete, error: fetchError } = await supabase
|
|
287
|
-
.from('messages')
|
|
288
|
-
.select('id')
|
|
289
|
-
.eq('conversation_id', conversationId)
|
|
290
|
-
.limit(BATCH_DELETE_SIZE);
|
|
291
|
-
|
|
292
|
-
if (fetchError) {
|
|
293
|
-
return { success: false, error: `Failed to fetch messages: ${fetchError.message}` };
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
if (!messagesToDelete || messagesToDelete.length === 0) {
|
|
297
|
-
hasMore = false;
|
|
298
|
-
break;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
const idsToDelete = messagesToDelete.map((m) => m.id);
|
|
302
|
-
|
|
303
|
-
// Delete the batch
|
|
304
|
-
const { error: deleteError } = await supabase
|
|
305
|
-
.from('messages')
|
|
306
|
-
.delete()
|
|
307
|
-
.in('id', idsToDelete);
|
|
308
|
-
|
|
309
|
-
if (deleteError) {
|
|
310
|
-
return { success: false, error: `Failed to delete messages: ${deleteError.message}` };
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
totalDeleted += idsToDelete.length;
|
|
314
|
-
|
|
315
|
-
// Check if we got a full batch (might be more)
|
|
316
|
-
hasMore = messagesToDelete.length === BATCH_DELETE_SIZE;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
return { success: true, deletedCount: totalDeleted };
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
export const messagesService = {
|
|
323
|
-
storeMessage,
|
|
324
|
-
listMessages,
|
|
325
|
-
deleteUserMessage,
|
|
326
|
-
clearConversationMessages,
|
|
327
|
-
};
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { api } from '@vibefast/backend/_generated/api';
|
|
2
|
-
import type { Id } from '@vibefast/backend/_generated/dataModel';
|
|
3
|
-
import { useAction, useQuery } from 'convex/react';
|
|
4
|
-
|
|
5
|
-
const skipToken = 'skip' as const;
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Image Analyzer API Gateway
|
|
9
|
-
*
|
|
10
|
-
* This gateway provides typed wrappers around Convex image analysis functions.
|
|
11
|
-
* It handles AI-powered image analysis with configurable goals and feedback styles.
|
|
12
|
-
*
|
|
13
|
-
* Usage:
|
|
14
|
-
* - Import this gateway in feature code: `import { imageAnalyzerApi } from '@/platform/api/image-analyzer'`
|
|
15
|
-
* - Never import from `@vibefast/backend/_generated/api` directly in feature code
|
|
16
|
-
*/
|
|
17
|
-
export const imageAnalyzerApi = {
|
|
18
|
-
/**
|
|
19
|
-
* Start image analysis action
|
|
20
|
-
*
|
|
21
|
-
* @returns Promise with analysis ID
|
|
22
|
-
*/
|
|
23
|
-
useAnalyzeImages() {
|
|
24
|
-
return useAction(api['imageAnalysis/index'].analyzeImages);
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Get analysis by ID
|
|
29
|
-
*/
|
|
30
|
-
useGetAnalysisById(analysisId?: Id<'imageAnalyses'> | null) {
|
|
31
|
-
return useQuery(
|
|
32
|
-
api['imageAnalysis/index'].getAnalysisById,
|
|
33
|
-
analysisId ? { analysisId } : skipToken,
|
|
34
|
-
);
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Get full analysis with image URLs
|
|
39
|
-
*/
|
|
40
|
-
useGetImageAnalysis(analysisId?: Id<'imageAnalyses'> | null) {
|
|
41
|
-
return useQuery(
|
|
42
|
-
api['imageAnalysis/index'].getImageAnalysis,
|
|
43
|
-
analysisId ? { analysisId } : skipToken,
|
|
44
|
-
);
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Get user's analysis history with pagination
|
|
49
|
-
*/
|
|
50
|
-
useGetUserImageAnalyses(
|
|
51
|
-
paginationOpts: { numItems: number; cursor: string | null } = {
|
|
52
|
-
numItems: 50,
|
|
53
|
-
cursor: null,
|
|
54
|
-
},
|
|
55
|
-
) {
|
|
56
|
-
return useQuery(api['imageAnalysis/index'].getUserImageAnalyses, {
|
|
57
|
-
paginationOpts,
|
|
58
|
-
});
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export type ImageAnalyzerApi = typeof imageAnalyzerApi;
|