ugly-app 0.1.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.
- package/.claude/settings.local.json +18 -0
- package/.env.example +9 -0
- package/.yarn-metadata.json +113 -0
- package/README.md +709 -0
- package/dist/cli/authCommands.d.ts +6 -0
- package/dist/cli/authCommands.d.ts.map +1 -0
- package/dist/cli/authCommands.js +50 -0
- package/dist/cli/authCommands.js.map +1 -0
- package/dist/cli/build.d.ts +2 -0
- package/dist/cli/build.d.ts.map +1 -0
- package/dist/cli/build.js +26 -0
- package/dist/cli/build.js.map +1 -0
- package/dist/cli/configure.d.ts +2 -0
- package/dist/cli/configure.d.ts.map +1 -0
- package/dist/cli/configure.js +53 -0
- package/dist/cli/configure.js.map +1 -0
- package/dist/cli/dev.d.ts +2 -0
- package/dist/cli/dev.d.ts.map +1 -0
- package/dist/cli/dev.js +66 -0
- package/dist/cli/dev.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +340 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/initDb.d.ts +2 -0
- package/dist/cli/initDb.d.ts.map +1 -0
- package/dist/cli/initDb.js +110 -0
- package/dist/cli/initDb.js.map +1 -0
- package/dist/cli/logQuery.d.ts +12 -0
- package/dist/cli/logQuery.d.ts.map +1 -0
- package/dist/cli/logQuery.js +176 -0
- package/dist/cli/logQuery.js.map +1 -0
- package/dist/cli/migrate.d.ts +4 -0
- package/dist/cli/migrate.d.ts.map +1 -0
- package/dist/cli/migrate.js +62 -0
- package/dist/cli/migrate.js.map +1 -0
- package/dist/cli/publishAssets.d.ts +6 -0
- package/dist/cli/publishAssets.d.ts.map +1 -0
- package/dist/cli/publishAssets.js +87 -0
- package/dist/cli/publishAssets.js.map +1 -0
- package/dist/cli/purgeAssets.d.ts +2 -0
- package/dist/cli/purgeAssets.d.ts.map +1 -0
- package/dist/cli/purgeAssets.js +36 -0
- package/dist/cli/purgeAssets.js.map +1 -0
- package/dist/cli/scaffold.d.ts +2 -0
- package/dist/cli/scaffold.d.ts.map +1 -0
- package/dist/cli/scaffold.js +37 -0
- package/dist/cli/scaffold.js.map +1 -0
- package/dist/cli/serverLogQuery.d.ts +6 -0
- package/dist/cli/serverLogQuery.d.ts.map +1 -0
- package/dist/cli/serverLogQuery.js +76 -0
- package/dist/cli/serverLogQuery.js.map +1 -0
- package/dist/cli/storageClient.d.ts +3 -0
- package/dist/cli/storageClient.d.ts.map +1 -0
- package/dist/cli/storageClient.js +22 -0
- package/dist/cli/storageClient.js.map +1 -0
- package/dist/cli/uglyappConfig.d.ts +29 -0
- package/dist/cli/uglyappConfig.d.ts.map +1 -0
- package/dist/cli/uglyappConfig.js +125 -0
- package/dist/cli/uglyappConfig.js.map +1 -0
- package/dist/client/AppProvider.d.ts +32 -0
- package/dist/client/AppProvider.d.ts.map +1 -0
- package/dist/client/AppProvider.js +126 -0
- package/dist/client/AppProvider.js.map +1 -0
- package/dist/client/ErrorLog.d.ts +16 -0
- package/dist/client/ErrorLog.d.ts.map +1 -0
- package/dist/client/ErrorLog.js +28 -0
- package/dist/client/ErrorLog.js.map +1 -0
- package/dist/client/FeedbackContext.d.ts +7 -0
- package/dist/client/FeedbackContext.d.ts.map +1 -0
- package/dist/client/FeedbackContext.js +15 -0
- package/dist/client/FeedbackContext.js.map +1 -0
- package/dist/client/Logger.d.ts +2 -0
- package/dist/client/Logger.d.ts.map +1 -0
- package/dist/client/Logger.js +72 -0
- package/dist/client/Logger.js.map +1 -0
- package/dist/client/LoginPopup.d.ts +7 -0
- package/dist/client/LoginPopup.d.ts.map +1 -0
- package/dist/client/LoginPopup.js +55 -0
- package/dist/client/LoginPopup.js.map +1 -0
- package/dist/client/Router.d.ts +56 -0
- package/dist/client/Router.d.ts.map +1 -0
- package/dist/client/Router.js +224 -0
- package/dist/client/Router.js.map +1 -0
- package/dist/client/Screenshot.d.ts +7 -0
- package/dist/client/Screenshot.d.ts.map +1 -0
- package/dist/client/Screenshot.js +54 -0
- package/dist/client/Screenshot.js.map +1 -0
- package/dist/client/ViewFlipper.d.ts +16 -0
- package/dist/client/ViewFlipper.d.ts.map +1 -0
- package/dist/client/ViewFlipper.js +156 -0
- package/dist/client/ViewFlipper.js.map +1 -0
- package/dist/client/animation/Animated.d.ts +14 -0
- package/dist/client/animation/Animated.d.ts.map +1 -0
- package/dist/client/animation/Animated.js +97 -0
- package/dist/client/animation/Animated.js.map +1 -0
- package/dist/client/animation/animatedValue.d.ts +41 -0
- package/dist/client/animation/animatedValue.d.ts.map +1 -0
- package/dist/client/animation/animatedValue.js +123 -0
- package/dist/client/animation/animatedValue.js.map +1 -0
- package/dist/client/animations/FadeIn.d.ts +6 -0
- package/dist/client/animations/FadeIn.d.ts.map +1 -0
- package/dist/client/animations/FadeIn.js +24 -0
- package/dist/client/animations/FadeIn.js.map +1 -0
- package/dist/client/animations/SlideFromBottom.d.ts +6 -0
- package/dist/client/animations/SlideFromBottom.d.ts.map +1 -0
- package/dist/client/animations/SlideFromBottom.js +24 -0
- package/dist/client/animations/SlideFromBottom.js.map +1 -0
- package/dist/client/animations/SlideFromRight.d.ts +6 -0
- package/dist/client/animations/SlideFromRight.d.ts.map +1 -0
- package/dist/client/animations/SlideFromRight.js +24 -0
- package/dist/client/animations/SlideFromRight.js.map +1 -0
- package/dist/client/audio/AudioPlayer.d.ts +11 -0
- package/dist/client/audio/AudioPlayer.d.ts.map +1 -0
- package/dist/client/audio/AudioPlayer.js +51 -0
- package/dist/client/audio/AudioPlayer.js.map +1 -0
- package/dist/client/audio/AudioRecorder.d.ts +11 -0
- package/dist/client/audio/AudioRecorder.d.ts.map +1 -0
- package/dist/client/audio/AudioRecorder.js +66 -0
- package/dist/client/audio/AudioRecorder.js.map +1 -0
- package/dist/client/audio/audioPlayer.worklet.d.ts +2 -0
- package/dist/client/audio/audioPlayer.worklet.d.ts.map +1 -0
- package/dist/client/audio/audioPlayer.worklet.js +46 -0
- package/dist/client/audio/audioPlayer.worklet.js.map +1 -0
- package/dist/client/audio/useSTT.d.ts +19 -0
- package/dist/client/audio/useSTT.d.ts.map +1 -0
- package/dist/client/audio/useSTT.js +91 -0
- package/dist/client/audio/useSTT.js.map +1 -0
- package/dist/client/audio/useTTS.d.ts +17 -0
- package/dist/client/audio/useTTS.d.ts.map +1 -0
- package/dist/client/audio/useTTS.js +70 -0
- package/dist/client/audio/useTTS.js.map +1 -0
- package/dist/client/components/Button.d.ts +22 -0
- package/dist/client/components/Button.d.ts.map +1 -0
- package/dist/client/components/Button.js +27 -0
- package/dist/client/components/Button.js.map +1 -0
- package/dist/client/components/Card.d.ts +8 -0
- package/dist/client/components/Card.d.ts.map +1 -0
- package/dist/client/components/Card.js +6 -0
- package/dist/client/components/Card.js.map +1 -0
- package/dist/client/components/EnumInput.d.ts +16 -0
- package/dist/client/components/EnumInput.d.ts.map +1 -0
- package/dist/client/components/EnumInput.js +11 -0
- package/dist/client/components/EnumInput.js.map +1 -0
- package/dist/client/components/FeedbackButton.d.ts +9 -0
- package/dist/client/components/FeedbackButton.d.ts.map +1 -0
- package/dist/client/components/FeedbackButton.js +112 -0
- package/dist/client/components/FeedbackButton.js.map +1 -0
- package/dist/client/components/Header.d.ts +10 -0
- package/dist/client/components/Header.d.ts.map +1 -0
- package/dist/client/components/Header.js +12 -0
- package/dist/client/components/Header.js.map +1 -0
- package/dist/client/components/Image.d.ts +20 -0
- package/dist/client/components/Image.d.ts.map +1 -0
- package/dist/client/components/Image.js +12 -0
- package/dist/client/components/Image.js.map +1 -0
- package/dist/client/components/Input.d.ts +14 -0
- package/dist/client/components/Input.d.ts.map +1 -0
- package/dist/client/components/Input.js +19 -0
- package/dist/client/components/Input.js.map +1 -0
- package/dist/client/components/Modal.d.ts +9 -0
- package/dist/client/components/Modal.d.ts.map +1 -0
- package/dist/client/components/Modal.js +19 -0
- package/dist/client/components/Modal.js.map +1 -0
- package/dist/client/components/PageLayout.d.ts +9 -0
- package/dist/client/components/PageLayout.d.ts.map +1 -0
- package/dist/client/components/PageLayout.js +6 -0
- package/dist/client/components/PageLayout.js.map +1 -0
- package/dist/client/components/Panel.d.ts +8 -0
- package/dist/client/components/Panel.d.ts.map +1 -0
- package/dist/client/components/Panel.js +5 -0
- package/dist/client/components/Panel.js.map +1 -0
- package/dist/client/components/PopupPanel.d.ts +9 -0
- package/dist/client/components/PopupPanel.d.ts.map +1 -0
- package/dist/client/components/PopupPanel.js +5 -0
- package/dist/client/components/PopupPanel.js.map +1 -0
- package/dist/client/components/Pressable.d.ts +14 -0
- package/dist/client/components/Pressable.d.ts.map +1 -0
- package/dist/client/components/Pressable.js +8 -0
- package/dist/client/components/Pressable.js.map +1 -0
- package/dist/client/components/ScrollView.d.ts +9 -0
- package/dist/client/components/ScrollView.d.ts.map +1 -0
- package/dist/client/components/ScrollView.js +7 -0
- package/dist/client/components/ScrollView.js.map +1 -0
- package/dist/client/components/SettingGroup.d.ts +10 -0
- package/dist/client/components/SettingGroup.d.ts.map +1 -0
- package/dist/client/components/SettingGroup.js +8 -0
- package/dist/client/components/SettingGroup.js.map +1 -0
- package/dist/client/components/Text.d.ts +21 -0
- package/dist/client/components/Text.d.ts.map +1 -0
- package/dist/client/components/Text.js +43 -0
- package/dist/client/components/Text.js.map +1 -0
- package/dist/client/components/Toast.d.ts +10 -0
- package/dist/client/components/Toast.d.ts.map +1 -0
- package/dist/client/components/Toast.js +21 -0
- package/dist/client/components/Toast.js.map +1 -0
- package/dist/client/components/View.d.ts +9 -0
- package/dist/client/components/View.d.ts.map +1 -0
- package/dist/client/components/View.js +5 -0
- package/dist/client/components/View.js.map +1 -0
- package/dist/client/components/index.d.ts +33 -0
- package/dist/client/components/index.d.ts.map +1 -0
- package/dist/client/components/index.js +17 -0
- package/dist/client/components/index.js.map +1 -0
- package/dist/client/components/zIndex.d.ts +10 -0
- package/dist/client/components/zIndex.d.ts.map +1 -0
- package/dist/client/components/zIndex.js +12 -0
- package/dist/client/components/zIndex.js.map +1 -0
- package/dist/client/createSocket.d.ts +26 -0
- package/dist/client/createSocket.d.ts.map +1 -0
- package/dist/client/createSocket.js +138 -0
- package/dist/client/createSocket.js.map +1 -0
- package/dist/client/index.d.ts +27 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +20 -0
- package/dist/client/index.js.map +1 -0
- package/dist/playwright/index.d.ts +16 -0
- package/dist/playwright/index.d.ts.map +1 -0
- package/dist/playwright/index.js +32 -0
- package/dist/playwright/index.js.map +1 -0
- package/dist/server/App.d.ts +43 -0
- package/dist/server/App.d.ts.map +1 -0
- package/dist/server/App.js +215 -0
- package/dist/server/App.js.map +1 -0
- package/dist/server/Auth.d.ts +21 -0
- package/dist/server/Auth.d.ts.map +1 -0
- package/dist/server/Auth.js +171 -0
- package/dist/server/Auth.js.map +1 -0
- package/dist/server/Cache.d.ts +38 -0
- package/dist/server/Cache.d.ts.map +1 -0
- package/dist/server/Cache.js +113 -0
- package/dist/server/Cache.js.map +1 -0
- package/dist/server/ChangeStream.d.ts +22 -0
- package/dist/server/ChangeStream.d.ts.map +1 -0
- package/dist/server/ChangeStream.js +93 -0
- package/dist/server/ChangeStream.js.map +1 -0
- package/dist/server/DB.d.ts +17 -0
- package/dist/server/DB.d.ts.map +1 -0
- package/dist/server/DB.js +442 -0
- package/dist/server/DB.js.map +1 -0
- package/dist/server/Email.d.ts +22 -0
- package/dist/server/Email.d.ts.map +1 -0
- package/dist/server/Email.js +64 -0
- package/dist/server/Email.js.map +1 -0
- package/dist/server/EmailTemplate.d.ts +17 -0
- package/dist/server/EmailTemplate.d.ts.map +1 -0
- package/dist/server/EmailTemplate.js +29 -0
- package/dist/server/EmailTemplate.js.map +1 -0
- package/dist/server/FeedbackReport.d.ts +19 -0
- package/dist/server/FeedbackReport.d.ts.map +1 -0
- package/dist/server/FeedbackReport.js +94 -0
- package/dist/server/FeedbackReport.js.map +1 -0
- package/dist/server/ImageGen.d.ts +5 -0
- package/dist/server/ImageGen.d.ts.map +1 -0
- package/dist/server/ImageGen.js +5 -0
- package/dist/server/ImageGen.js.map +1 -0
- package/dist/server/Logging.d.ts +59 -0
- package/dist/server/Logging.d.ts.map +1 -0
- package/dist/server/Logging.js +293 -0
- package/dist/server/Logging.js.map +1 -0
- package/dist/server/Nats.d.ts +56 -0
- package/dist/server/Nats.d.ts.map +1 -0
- package/dist/server/Nats.js +162 -0
- package/dist/server/Nats.js.map +1 -0
- package/dist/server/NatsStore.d.ts +26 -0
- package/dist/server/NatsStore.d.ts.map +1 -0
- package/dist/server/NatsStore.js +86 -0
- package/dist/server/NatsStore.js.map +1 -0
- package/dist/server/PushNotification.d.ts +33 -0
- package/dist/server/PushNotification.d.ts.map +1 -0
- package/dist/server/PushNotification.js +131 -0
- package/dist/server/PushNotification.js.map +1 -0
- package/dist/server/RateLimit.d.ts +28 -0
- package/dist/server/RateLimit.d.ts.map +1 -0
- package/dist/server/RateLimit.js +101 -0
- package/dist/server/RateLimit.js.map +1 -0
- package/dist/server/Redis.d.ts +61 -0
- package/dist/server/Redis.d.ts.map +1 -0
- package/dist/server/Redis.js +226 -0
- package/dist/server/Redis.js.map +1 -0
- package/dist/server/Router.d.ts +27 -0
- package/dist/server/Router.d.ts.map +1 -0
- package/dist/server/Router.js +71 -0
- package/dist/server/Router.js.map +1 -0
- package/dist/server/Socket.d.ts +8 -0
- package/dist/server/Socket.d.ts.map +1 -0
- package/dist/server/Socket.js +278 -0
- package/dist/server/Socket.js.map +1 -0
- package/dist/server/SpendTracker.d.ts +18 -0
- package/dist/server/SpendTracker.d.ts.map +1 -0
- package/dist/server/SpendTracker.js +110 -0
- package/dist/server/SpendTracker.js.map +1 -0
- package/dist/server/Storage.d.ts +16 -0
- package/dist/server/Storage.d.ts.map +1 -0
- package/dist/server/Storage.js +95 -0
- package/dist/server/Storage.js.map +1 -0
- package/dist/server/TextGen.d.ts +5 -0
- package/dist/server/TextGen.d.ts.map +1 -0
- package/dist/server/TextGen.js +6 -0
- package/dist/server/TextGen.js.map +1 -0
- package/dist/server/WorkerQueue.d.ts +27 -0
- package/dist/server/WorkerQueue.d.ts.map +1 -0
- package/dist/server/WorkerQueue.js +147 -0
- package/dist/server/WorkerQueue.js.map +1 -0
- package/dist/server/ai/ImageGenClient.d.ts +6 -0
- package/dist/server/ai/ImageGenClient.d.ts.map +1 -0
- package/dist/server/ai/ImageGenClient.js +29 -0
- package/dist/server/ai/ImageGenClient.js.map +1 -0
- package/dist/server/ai/ProviderBalance.d.ts +15 -0
- package/dist/server/ai/ProviderBalance.d.ts.map +1 -0
- package/dist/server/ai/ProviderBalance.js +110 -0
- package/dist/server/ai/ProviderBalance.js.map +1 -0
- package/dist/server/ai/ProviderSelector.d.ts +13 -0
- package/dist/server/ai/ProviderSelector.d.ts.map +1 -0
- package/dist/server/ai/ProviderSelector.js +25 -0
- package/dist/server/ai/ProviderSelector.js.map +1 -0
- package/dist/server/ai/TextGenClient.d.ts +9 -0
- package/dist/server/ai/TextGenClient.d.ts.map +1 -0
- package/dist/server/ai/TextGenClient.js +72 -0
- package/dist/server/ai/TextGenClient.js.map +1 -0
- package/dist/server/ai/fallbacks.d.ts +9 -0
- package/dist/server/ai/fallbacks.d.ts.map +1 -0
- package/dist/server/ai/fallbacks.js +77 -0
- package/dist/server/ai/fallbacks.js.map +1 -0
- package/dist/server/ai/index.d.ts +12 -0
- package/dist/server/ai/index.d.ts.map +1 -0
- package/dist/server/ai/index.js +37 -0
- package/dist/server/ai/index.js.map +1 -0
- package/dist/server/ai/providers/Claude.d.ts +3 -0
- package/dist/server/ai/providers/Claude.d.ts.map +1 -0
- package/dist/server/ai/providers/Claude.js +124 -0
- package/dist/server/ai/providers/Claude.js.map +1 -0
- package/dist/server/ai/providers/FAL.d.ts +3 -0
- package/dist/server/ai/providers/FAL.d.ts.map +1 -0
- package/dist/server/ai/providers/FAL.js +24 -0
- package/dist/server/ai/providers/FAL.js.map +1 -0
- package/dist/server/ai/providers/Fireworks.d.ts +3 -0
- package/dist/server/ai/providers/Fireworks.d.ts.map +1 -0
- package/dist/server/ai/providers/Fireworks.js +79 -0
- package/dist/server/ai/providers/Fireworks.js.map +1 -0
- package/dist/server/ai/providers/Google.d.ts +3 -0
- package/dist/server/ai/providers/Google.d.ts.map +1 -0
- package/dist/server/ai/providers/Google.js +105 -0
- package/dist/server/ai/providers/Google.js.map +1 -0
- package/dist/server/ai/providers/GoogleImage.d.ts +3 -0
- package/dist/server/ai/providers/GoogleImage.d.ts.map +1 -0
- package/dist/server/ai/providers/GoogleImage.js +24 -0
- package/dist/server/ai/providers/GoogleImage.js.map +1 -0
- package/dist/server/ai/providers/Groq.d.ts +3 -0
- package/dist/server/ai/providers/Groq.d.ts.map +1 -0
- package/dist/server/ai/providers/Groq.js +99 -0
- package/dist/server/ai/providers/Groq.js.map +1 -0
- package/dist/server/ai/providers/Kie.d.ts +3 -0
- package/dist/server/ai/providers/Kie.d.ts.map +1 -0
- package/dist/server/ai/providers/Kie.js +79 -0
- package/dist/server/ai/providers/Kie.js.map +1 -0
- package/dist/server/ai/providers/KieImage.d.ts +3 -0
- package/dist/server/ai/providers/KieImage.d.ts.map +1 -0
- package/dist/server/ai/providers/KieImage.js +50 -0
- package/dist/server/ai/providers/KieImage.js.map +1 -0
- package/dist/server/ai/providers/OpenAIText.d.ts +3 -0
- package/dist/server/ai/providers/OpenAIText.d.ts.map +1 -0
- package/dist/server/ai/providers/OpenAIText.js +103 -0
- package/dist/server/ai/providers/OpenAIText.js.map +1 -0
- package/dist/server/ai/providers/Together.d.ts +3 -0
- package/dist/server/ai/providers/Together.d.ts.map +1 -0
- package/dist/server/ai/providers/Together.js +99 -0
- package/dist/server/ai/providers/Together.js.map +1 -0
- package/dist/server/ai/providers/TogetherImage.d.ts +3 -0
- package/dist/server/ai/providers/TogetherImage.d.ts.map +1 -0
- package/dist/server/ai/providers/TogetherImage.js +24 -0
- package/dist/server/ai/providers/TogetherImage.js.map +1 -0
- package/dist/server/ai/providers/Wavespeed.d.ts +3 -0
- package/dist/server/ai/providers/Wavespeed.d.ts.map +1 -0
- package/dist/server/ai/providers/Wavespeed.js +53 -0
- package/dist/server/ai/providers/Wavespeed.js.map +1 -0
- package/dist/server/ai/registry.d.ts +10 -0
- package/dist/server/ai/registry.d.ts.map +1 -0
- package/dist/server/ai/registry.js +26 -0
- package/dist/server/ai/registry.js.map +1 -0
- package/dist/server/ai/types.d.ts +67 -0
- package/dist/server/ai/types.d.ts.map +1 -0
- package/dist/server/ai/types.js +17 -0
- package/dist/server/ai/types.js.map +1 -0
- package/dist/server/audio/STTStream.d.ts +12 -0
- package/dist/server/audio/STTStream.d.ts.map +1 -0
- package/dist/server/audio/STTStream.js +59 -0
- package/dist/server/audio/STTStream.js.map +1 -0
- package/dist/server/audio/TTSStream.d.ts +16 -0
- package/dist/server/audio/TTSStream.d.ts.map +1 -0
- package/dist/server/audio/TTSStream.js +151 -0
- package/dist/server/audio/TTSStream.js.map +1 -0
- package/dist/server/audio/index.d.ts +5 -0
- package/dist/server/audio/index.d.ts.map +1 -0
- package/dist/server/audio/index.js +14 -0
- package/dist/server/audio/index.js.map +1 -0
- package/dist/server/audio/resample.d.ts +7 -0
- package/dist/server/audio/resample.d.ts.map +1 -0
- package/dist/server/audio/resample.js +17 -0
- package/dist/server/audio/resample.js.map +1 -0
- package/dist/server/audio/stt/GroqWhisper.d.ts +3 -0
- package/dist/server/audio/stt/GroqWhisper.d.ts.map +1 -0
- package/dist/server/audio/stt/GroqWhisper.js +91 -0
- package/dist/server/audio/stt/GroqWhisper.js.map +1 -0
- package/dist/server/audio/stt/registry.d.ts +5 -0
- package/dist/server/audio/stt/registry.d.ts.map +1 -0
- package/dist/server/audio/stt/registry.js +11 -0
- package/dist/server/audio/stt/registry.js.map +1 -0
- package/dist/server/audio/stt/types.d.ts +25 -0
- package/dist/server/audio/stt/types.d.ts.map +1 -0
- package/dist/server/audio/stt/types.js +2 -0
- package/dist/server/audio/stt/types.js.map +1 -0
- package/dist/server/audio/stt/vad.d.ts +18 -0
- package/dist/server/audio/stt/vad.d.ts.map +1 -0
- package/dist/server/audio/stt/vad.js +90 -0
- package/dist/server/audio/stt/vad.js.map +1 -0
- package/dist/server/audio/tts/InWorld.d.ts +3 -0
- package/dist/server/audio/tts/InWorld.d.ts.map +1 -0
- package/dist/server/audio/tts/InWorld.js +147 -0
- package/dist/server/audio/tts/InWorld.js.map +1 -0
- package/dist/server/audio/tts/providers/Azure.d.ts +3 -0
- package/dist/server/audio/tts/providers/Azure.d.ts.map +1 -0
- package/dist/server/audio/tts/providers/Azure.js +31 -0
- package/dist/server/audio/tts/providers/Azure.js.map +1 -0
- package/dist/server/audio/tts/registry.d.ts +6 -0
- package/dist/server/audio/tts/registry.d.ts.map +1 -0
- package/dist/server/audio/tts/registry.js +12 -0
- package/dist/server/audio/tts/registry.js.map +1 -0
- package/dist/server/audio/tts/types.d.ts +20 -0
- package/dist/server/audio/tts/types.d.ts.map +1 -0
- package/dist/server/audio/tts/types.js +2 -0
- package/dist/server/audio/tts/types.js.map +1 -0
- package/dist/server/billing/BillingGateway.d.ts +40 -0
- package/dist/server/billing/BillingGateway.d.ts.map +1 -0
- package/dist/server/billing/BillingGateway.js +116 -0
- package/dist/server/billing/BillingGateway.js.map +1 -0
- package/dist/server/billing/BillingLedger.d.ts +67 -0
- package/dist/server/billing/BillingLedger.d.ts.map +1 -0
- package/dist/server/billing/BillingLedger.js +214 -0
- package/dist/server/billing/BillingLedger.js.map +1 -0
- package/dist/server/billing/CreditStore.d.ts +58 -0
- package/dist/server/billing/CreditStore.d.ts.map +1 -0
- package/dist/server/billing/CreditStore.js +112 -0
- package/dist/server/billing/CreditStore.js.map +1 -0
- package/dist/server/billing/LimitEnforcer.d.ts +73 -0
- package/dist/server/billing/LimitEnforcer.d.ts.map +1 -0
- package/dist/server/billing/LimitEnforcer.js +276 -0
- package/dist/server/billing/LimitEnforcer.js.map +1 -0
- package/dist/server/billing/UserLimitCache.d.ts +35 -0
- package/dist/server/billing/UserLimitCache.d.ts.map +1 -0
- package/dist/server/billing/UserLimitCache.js +91 -0
- package/dist/server/billing/UserLimitCache.js.map +1 -0
- package/dist/server/billing/index.d.ts +6 -0
- package/dist/server/billing/index.d.ts.map +1 -0
- package/dist/server/billing/index.js +4 -0
- package/dist/server/billing/index.js.map +1 -0
- package/dist/server/billing/types.d.ts +95 -0
- package/dist/server/billing/types.d.ts.map +1 -0
- package/dist/server/billing/types.js +19 -0
- package/dist/server/billing/types.js.map +1 -0
- package/dist/server/embeddings/EmbeddingClient.d.ts +8 -0
- package/dist/server/embeddings/EmbeddingClient.d.ts.map +1 -0
- package/dist/server/embeddings/EmbeddingClient.js +30 -0
- package/dist/server/embeddings/EmbeddingClient.js.map +1 -0
- package/dist/server/embeddings/index.d.ts +5 -0
- package/dist/server/embeddings/index.d.ts.map +1 -0
- package/dist/server/embeddings/index.js +6 -0
- package/dist/server/embeddings/index.js.map +1 -0
- package/dist/server/embeddings/providers/OpenAI.d.ts +3 -0
- package/dist/server/embeddings/providers/OpenAI.d.ts.map +1 -0
- package/dist/server/embeddings/providers/OpenAI.js +28 -0
- package/dist/server/embeddings/providers/OpenAI.js.map +1 -0
- package/dist/server/embeddings/registry.d.ts +7 -0
- package/dist/server/embeddings/registry.d.ts.map +1 -0
- package/dist/server/embeddings/registry.js +15 -0
- package/dist/server/embeddings/registry.js.map +1 -0
- package/dist/server/embeddings/types.d.ts +12 -0
- package/dist/server/embeddings/types.d.ts.map +1 -0
- package/dist/server/embeddings/types.js +2 -0
- package/dist/server/embeddings/types.js.map +1 -0
- package/dist/server/index.d.ts +59 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +41 -0
- package/dist/server/index.js.map +1 -0
- package/dist/shared/Api.d.ts +40 -0
- package/dist/shared/Api.d.ts.map +1 -0
- package/dist/shared/Api.js +30 -0
- package/dist/shared/Api.js.map +1 -0
- package/dist/shared/Audio.d.ts +108 -0
- package/dist/shared/Audio.d.ts.map +1 -0
- package/dist/shared/Audio.js +3 -0
- package/dist/shared/Audio.js.map +1 -0
- package/dist/shared/DB.d.ts +339 -0
- package/dist/shared/DB.d.ts.map +1 -0
- package/dist/shared/DB.js +51 -0
- package/dist/shared/DB.js.map +1 -0
- package/dist/shared/Errors.d.ts +10 -0
- package/dist/shared/Errors.d.ts.map +1 -0
- package/dist/shared/Errors.js +19 -0
- package/dist/shared/Errors.js.map +1 -0
- package/dist/shared/FeedbackReport.d.ts +38 -0
- package/dist/shared/FeedbackReport.d.ts.map +1 -0
- package/dist/shared/FeedbackReport.js +14 -0
- package/dist/shared/FeedbackReport.js.map +1 -0
- package/dist/shared/Router.d.ts +28 -0
- package/dist/shared/Router.d.ts.map +1 -0
- package/dist/shared/Router.js +82 -0
- package/dist/shared/Router.js.map +1 -0
- package/dist/shared/Socket.d.ts +26 -0
- package/dist/shared/Socket.d.ts.map +1 -0
- package/dist/shared/Socket.js +2 -0
- package/dist/shared/Socket.js.map +1 -0
- package/dist/shared/index.d.ts +28 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +61 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/webrtc/index.d.ts +14 -0
- package/dist/webrtc/index.d.ts.map +1 -0
- package/dist/webrtc/index.js +24 -0
- package/dist/webrtc/index.js.map +1 -0
- package/package.json +97 -0
- package/templates/.claude/skills/assets.md +39 -0
- package/templates/.claude/skills/check-errors.md +35 -0
- package/templates/.claude/skills/check-feedback.md +23 -0
- package/templates/.claude/skills/check-perf.md +22 -0
- package/templates/.claude/skills/create-test-users.md +34 -0
- package/templates/.claude/skills/extend-api.md +37 -0
- package/templates/.claude/skills/fix-code.md +5 -0
- package/templates/.claude/skills/fix-errors.md +9 -0
- package/templates/.claude/skills/fix-feedback.md +8 -0
- package/templates/.claude/skills/fix-perf.md +8 -0
- package/templates/.claude/skills/uploads.md +38 -0
- package/templates/.claude/skills/use-ai.md +49 -0
- package/templates/.env.example +49 -0
- package/templates/.husky/pre-commit +2 -0
- package/templates/CLAUDE.md +199 -0
- package/templates/client/App.tsx +33 -0
- package/templates/client/index.html +14 -0
- package/templates/client/main.tsx +52 -0
- package/templates/client/pages/AuthDemoPage.tsx +85 -0
- package/templates/client/pages/HomePage.tsx +111 -0
- package/templates/client/pages/SearchPage.tsx +48 -0
- package/templates/client/pages/UserPage.tsx +33 -0
- package/templates/client/router.ts +4 -0
- package/templates/docker-compose.yml +50 -0
- package/templates/index.html +14 -0
- package/templates/package.json +56 -0
- package/templates/playwright.config.ts +21 -0
- package/templates/server/index.ts +24 -0
- package/templates/shared/api.ts +36 -0
- package/templates/shared/collections.ts +11 -0
- package/templates/shared/dbIndexes.ts +11 -0
- package/templates/shared/pages.ts +8 -0
- package/templates/vite.config.ts +17 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
const HOUR_MS = 3_600_000;
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// BillingLedger
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
export class BillingLedger {
|
|
6
|
+
db;
|
|
7
|
+
reconcileIntervalMs;
|
|
8
|
+
pending = [];
|
|
9
|
+
timer = null;
|
|
10
|
+
constructor(db, reconcileIntervalMs = 300_000) {
|
|
11
|
+
this.db = db;
|
|
12
|
+
this.reconcileIntervalMs = reconcileIntervalMs;
|
|
13
|
+
}
|
|
14
|
+
/** Expose pending buffer size (for testing). */
|
|
15
|
+
pendingCount() {
|
|
16
|
+
return this.pending.length;
|
|
17
|
+
}
|
|
18
|
+
/** Synchronously append a record to the in-memory buffer. */
|
|
19
|
+
add(record) {
|
|
20
|
+
const stored = {
|
|
21
|
+
id: record.id,
|
|
22
|
+
version: 1,
|
|
23
|
+
created: new Date(record.timestamp),
|
|
24
|
+
updated: new Date(record.timestamp),
|
|
25
|
+
userId: record.userId,
|
|
26
|
+
provider: record.provider,
|
|
27
|
+
model: record.model,
|
|
28
|
+
type: record.type,
|
|
29
|
+
costUsd: record.costUsd,
|
|
30
|
+
inputTokens: record.inputTokens,
|
|
31
|
+
outputTokens: record.outputTokens,
|
|
32
|
+
imageCount: record.imageCount,
|
|
33
|
+
imageWidth: record.imageWidth,
|
|
34
|
+
imageHeight: record.imageHeight,
|
|
35
|
+
imageSteps: record.imageSteps,
|
|
36
|
+
audioSeconds: record.audioSeconds,
|
|
37
|
+
timestamp: record.timestamp,
|
|
38
|
+
};
|
|
39
|
+
this.pending.push(stored);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Sum costUsd for records in [now-windowMs, now], combining DB + in-memory pending.
|
|
43
|
+
* Pending records are NOT in DB yet — no double-counting.
|
|
44
|
+
*/
|
|
45
|
+
async getWindowSpend(windowMs, filters) {
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
const from = filters?.from ?? now - windowMs;
|
|
48
|
+
const to = filters?.to ?? now;
|
|
49
|
+
const filter = {
|
|
50
|
+
timestamp: { $gte: from, $lte: to },
|
|
51
|
+
};
|
|
52
|
+
if (filters?.userId !== undefined)
|
|
53
|
+
filter.userId = filters.userId;
|
|
54
|
+
if (filters?.provider !== undefined)
|
|
55
|
+
filter.provider = filters.provider;
|
|
56
|
+
if (filters?.type !== undefined)
|
|
57
|
+
filter.type = filters.type;
|
|
58
|
+
const dbRecords = await this.db.getDocs(filter, {});
|
|
59
|
+
const dbTotal = dbRecords.reduce((sum, r) => sum + r.costUsd, 0);
|
|
60
|
+
const pendingTotal = this.pending
|
|
61
|
+
.filter((r) => {
|
|
62
|
+
if (r.timestamp < from || r.timestamp > to)
|
|
63
|
+
return false;
|
|
64
|
+
if (filters?.userId !== undefined && r.userId !== filters.userId)
|
|
65
|
+
return false;
|
|
66
|
+
if (filters?.provider !== undefined && r.provider !== filters.provider)
|
|
67
|
+
return false;
|
|
68
|
+
if (filters?.type !== undefined && r.type !== filters.type)
|
|
69
|
+
return false;
|
|
70
|
+
return true;
|
|
71
|
+
})
|
|
72
|
+
.reduce((sum, r) => sum + r.costUsd, 0);
|
|
73
|
+
return dbTotal + pendingTotal;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Upsert all pending records to MongoDB, then clear the buffer.
|
|
77
|
+
* Atomic snapshot: concurrent adds after the snapshot are safe.
|
|
78
|
+
* On failure: failed records are prepended back to pending.
|
|
79
|
+
*/
|
|
80
|
+
async reconcile() {
|
|
81
|
+
if (this.pending.length === 0)
|
|
82
|
+
return;
|
|
83
|
+
// Atomic snapshot
|
|
84
|
+
const toWrite = this.pending;
|
|
85
|
+
this.pending = [];
|
|
86
|
+
const failed = [];
|
|
87
|
+
for (const record of toWrite) {
|
|
88
|
+
try {
|
|
89
|
+
await this.db.setDoc(record.id, record);
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
failed.push(record);
|
|
93
|
+
console.error('[BillingLedger] Failed to reconcile record', record.id, err);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (failed.length > 0) {
|
|
97
|
+
// Prepend failed records so they're retried first next cycle
|
|
98
|
+
this.pending = [...failed, ...this.pending];
|
|
99
|
+
}
|
|
100
|
+
if (this.pending.length > 1000) {
|
|
101
|
+
console.warn('[BillingLedger] pending buffer exceeds 1000 records', {
|
|
102
|
+
pendingCount: this.pending.length,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Query billing history merging DB records and in-memory pending.
|
|
108
|
+
* Sorted by timestamp DESC, id ASC. Limit/skip applied to merged result.
|
|
109
|
+
*/
|
|
110
|
+
async getHistory(filters = {}) {
|
|
111
|
+
const limit = Math.min(filters.limit ?? 100, 1000);
|
|
112
|
+
const skip = filters.skip ?? 0;
|
|
113
|
+
const filter = {};
|
|
114
|
+
if (filters.from !== undefined || filters.to !== undefined) {
|
|
115
|
+
filter.timestamp = {};
|
|
116
|
+
if (filters.from !== undefined)
|
|
117
|
+
filter.timestamp['$gte'] = filters.from;
|
|
118
|
+
if (filters.to !== undefined)
|
|
119
|
+
filter.timestamp['$lte'] = filters.to;
|
|
120
|
+
}
|
|
121
|
+
if (filters.userId !== undefined)
|
|
122
|
+
filter.userId = filters.userId;
|
|
123
|
+
if (filters.provider !== undefined)
|
|
124
|
+
filter.provider = filters.provider;
|
|
125
|
+
if (filters.type !== undefined)
|
|
126
|
+
filter.type = filters.type;
|
|
127
|
+
// Fetch more from DB than requested to account for pending records shifting pagination
|
|
128
|
+
const dbRecords = await this.db.getDocs(filter, {
|
|
129
|
+
sort: { timestamp: -1, id: 1 },
|
|
130
|
+
limit: limit + this.pending.length + skip,
|
|
131
|
+
});
|
|
132
|
+
const pendingMatched = this.pending.filter((r) => {
|
|
133
|
+
if (filters.from !== undefined && r.timestamp < filters.from)
|
|
134
|
+
return false;
|
|
135
|
+
if (filters.to !== undefined && r.timestamp > filters.to)
|
|
136
|
+
return false;
|
|
137
|
+
if (filters.userId !== undefined && r.userId !== filters.userId)
|
|
138
|
+
return false;
|
|
139
|
+
if (filters.provider !== undefined && r.provider !== filters.provider)
|
|
140
|
+
return false;
|
|
141
|
+
if (filters.type !== undefined && r.type !== filters.type)
|
|
142
|
+
return false;
|
|
143
|
+
return true;
|
|
144
|
+
});
|
|
145
|
+
// Merge DB + pending: DB records don't include pending (not yet written)
|
|
146
|
+
const all = [
|
|
147
|
+
...dbRecords.map(storedToPublic),
|
|
148
|
+
...pendingMatched.map(storedToPublic),
|
|
149
|
+
];
|
|
150
|
+
// Sort by timestamp DESC, then id ASC as tiebreaker
|
|
151
|
+
all.sort((a, b) => {
|
|
152
|
+
const tsDiff = b.timestamp - a.timestamp;
|
|
153
|
+
if (tsDiff !== 0)
|
|
154
|
+
return tsDiff;
|
|
155
|
+
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
156
|
+
});
|
|
157
|
+
return all.slice(skip, skip + limit);
|
|
158
|
+
}
|
|
159
|
+
/** Start the periodic reconcile timer. */
|
|
160
|
+
start() {
|
|
161
|
+
if (this.timer)
|
|
162
|
+
return;
|
|
163
|
+
this.timer = setInterval(() => {
|
|
164
|
+
void this.reconcile();
|
|
165
|
+
}, this.reconcileIntervalMs);
|
|
166
|
+
}
|
|
167
|
+
/** Stop the timer and flush pending records to DB. */
|
|
168
|
+
async shutdown() {
|
|
169
|
+
if (this.timer) {
|
|
170
|
+
clearInterval(this.timer);
|
|
171
|
+
this.timer = null;
|
|
172
|
+
}
|
|
173
|
+
await this.reconcile();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
// Internal helpers
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
function storedToPublic(r) {
|
|
180
|
+
const record = {
|
|
181
|
+
id: r.id,
|
|
182
|
+
provider: r.provider,
|
|
183
|
+
model: r.model,
|
|
184
|
+
type: r.type,
|
|
185
|
+
costUsd: r.costUsd,
|
|
186
|
+
timestamp: r.timestamp,
|
|
187
|
+
};
|
|
188
|
+
if (r.userId !== undefined)
|
|
189
|
+
record.userId = r.userId;
|
|
190
|
+
if (r.inputTokens !== undefined)
|
|
191
|
+
record.inputTokens = r.inputTokens;
|
|
192
|
+
if (r.outputTokens !== undefined)
|
|
193
|
+
record.outputTokens = r.outputTokens;
|
|
194
|
+
if (r.imageCount !== undefined)
|
|
195
|
+
record.imageCount = r.imageCount;
|
|
196
|
+
if (r.imageWidth !== undefined)
|
|
197
|
+
record.imageWidth = r.imageWidth;
|
|
198
|
+
if (r.imageHeight !== undefined)
|
|
199
|
+
record.imageHeight = r.imageHeight;
|
|
200
|
+
if (r.imageSteps !== undefined)
|
|
201
|
+
record.imageSteps = r.imageSteps;
|
|
202
|
+
if (r.audioSeconds !== undefined)
|
|
203
|
+
record.audioSeconds = r.audioSeconds;
|
|
204
|
+
return record;
|
|
205
|
+
}
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
// Convenience: how many ms in each window
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
export const WINDOW_MS = {
|
|
210
|
+
HOUR: HOUR_MS,
|
|
211
|
+
DAY: 24 * HOUR_MS,
|
|
212
|
+
WEEK: 7 * 24 * HOUR_MS,
|
|
213
|
+
};
|
|
214
|
+
//# sourceMappingURL=BillingLedger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BillingLedger.js","sourceRoot":"","sources":["../../../src/server/billing/BillingLedger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { UserCredit } from './types.js';
|
|
2
|
+
export interface CreditDB {
|
|
3
|
+
/** Atomically increment balanceUsd by delta (may be negative). Returns new balance. */
|
|
4
|
+
incrementCredit(userId: string, deltaUsd: number): Promise<number>;
|
|
5
|
+
/** Get current balance. Returns 0 if no record exists. */
|
|
6
|
+
getCredit(userId: string): Promise<number>;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Manages pre-paid credit balances for users.
|
|
10
|
+
*
|
|
11
|
+
* Credits are stored in MongoDB (`userCredits` collection) and cached in-memory
|
|
12
|
+
* with a short TTL. Deductions use optimistic in-memory decrement + async DB write;
|
|
13
|
+
* grants are written directly to DB and then invalidate the cache.
|
|
14
|
+
*/
|
|
15
|
+
export declare class CreditStore {
|
|
16
|
+
private readonly db;
|
|
17
|
+
private readonly cache;
|
|
18
|
+
private readonly ttlMs;
|
|
19
|
+
private readonly maxSize;
|
|
20
|
+
constructor(db: CreditDB, ttlMs?: number, // 1 hour
|
|
21
|
+
maxSize?: number);
|
|
22
|
+
/**
|
|
23
|
+
* Grant credits to a user (additive). Writes directly to DB; invalidates cache.
|
|
24
|
+
* Safe to call multiple times — amounts are accumulated.
|
|
25
|
+
*/
|
|
26
|
+
grant(userId: string, amountUsd: number): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Get current credit balance (cached with TTL).
|
|
29
|
+
*/
|
|
30
|
+
getBalance(userId: string): Promise<number>;
|
|
31
|
+
/**
|
|
32
|
+
* Attempt to deduct costUsd from the user's credit balance.
|
|
33
|
+
* Returns true if deducted successfully, false if balance is insufficient.
|
|
34
|
+
* Uses optimistic in-memory decrement; DB write is async (fire-and-forget with logging).
|
|
35
|
+
*/
|
|
36
|
+
tryDeduct(userId: string, costUsd: number): Promise<boolean>;
|
|
37
|
+
/** Invalidate cached balance for a user (e.g. after external grant). */
|
|
38
|
+
invalidate(userId: string): void;
|
|
39
|
+
private setCache;
|
|
40
|
+
}
|
|
41
|
+
export interface CreditDocDB {
|
|
42
|
+
/** Upsert a userCredits document. */
|
|
43
|
+
setDoc(id: string, doc: UserCredit): Promise<void>;
|
|
44
|
+
/** Get a userCredits document by id. */
|
|
45
|
+
getDoc(id: string): Promise<UserCredit | null>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* MongoDB-backed CreditDB implementation.
|
|
49
|
+
* Uses findOneAndUpdate-style atomic increment via a helper that performs
|
|
50
|
+
* a read-modify-write with the document's balanceUsd field.
|
|
51
|
+
*/
|
|
52
|
+
export declare class MongoCreditDB implements CreditDB {
|
|
53
|
+
private readonly db;
|
|
54
|
+
constructor(db: CreditDocDB);
|
|
55
|
+
incrementCredit(userId: string, deltaUsd: number): Promise<number>;
|
|
56
|
+
getCredit(userId: string): Promise<number>;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=CreditStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CreditStore.d.ts","sourceRoot":"","sources":["../../../src/server/billing/CreditStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAM7C,MAAM,WAAW,QAAQ;IACvB,uFAAuF;IACvF,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnE,0DAA0D;IAC1D,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC5C;AAWD;;;;;;GAMG;AACH,qBAAa,WAAW;IAMpB,OAAO,CAAC,QAAQ,CAAC,EAAE;IALrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;IACvD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAGd,EAAE,EAAE,QAAQ,EAC7B,KAAK,SAAY,EAAE,SAAS;IAC5B,OAAO,SAAS;IAMlB;;;OAGG;IACG,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY7D;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUjD;;;;OAIG;IACG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA4BlE,wEAAwE;IACxE,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAQhC,OAAO,CAAC,QAAQ;CAQjB;AAMD,MAAM,WAAW,WAAW;IAC1B,qCAAqC;IACrC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,wCAAwC;IACxC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;CAChD;AAED;;;;GAIG;AACH,qBAAa,aAAc,YAAW,QAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,WAAW;IAEtC,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAalE,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAIjD"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages pre-paid credit balances for users.
|
|
3
|
+
*
|
|
4
|
+
* Credits are stored in MongoDB (`userCredits` collection) and cached in-memory
|
|
5
|
+
* with a short TTL. Deductions use optimistic in-memory decrement + async DB write;
|
|
6
|
+
* grants are written directly to DB and then invalidate the cache.
|
|
7
|
+
*/
|
|
8
|
+
export class CreditStore {
|
|
9
|
+
db;
|
|
10
|
+
cache = new Map();
|
|
11
|
+
ttlMs;
|
|
12
|
+
maxSize;
|
|
13
|
+
constructor(db, ttlMs = 3_600_000, // 1 hour
|
|
14
|
+
maxSize = 10_000) {
|
|
15
|
+
this.db = db;
|
|
16
|
+
this.ttlMs = ttlMs;
|
|
17
|
+
this.maxSize = maxSize;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Grant credits to a user (additive). Writes directly to DB; invalidates cache.
|
|
21
|
+
* Safe to call multiple times — amounts are accumulated.
|
|
22
|
+
*/
|
|
23
|
+
async grant(userId, amountUsd) {
|
|
24
|
+
if (!Number.isFinite(amountUsd) || amountUsd <= 0) {
|
|
25
|
+
throw new Error(`[CreditStore] amountUsd must be finite positive, got ${String(amountUsd)}`);
|
|
26
|
+
}
|
|
27
|
+
const newBalance = await this.db.incrementCredit(userId, amountUsd);
|
|
28
|
+
this.setCache(userId, newBalance);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get current credit balance (cached with TTL).
|
|
32
|
+
*/
|
|
33
|
+
async getBalance(userId) {
|
|
34
|
+
const cached = this.cache.get(userId);
|
|
35
|
+
if (cached && Date.now() - cached.fetchedAt < this.ttlMs) {
|
|
36
|
+
return cached.balanceUsd;
|
|
37
|
+
}
|
|
38
|
+
const balance = await this.db.getCredit(userId);
|
|
39
|
+
this.setCache(userId, balance);
|
|
40
|
+
return balance;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Attempt to deduct costUsd from the user's credit balance.
|
|
44
|
+
* Returns true if deducted successfully, false if balance is insufficient.
|
|
45
|
+
* Uses optimistic in-memory decrement; DB write is async (fire-and-forget with logging).
|
|
46
|
+
*/
|
|
47
|
+
async tryDeduct(userId, costUsd) {
|
|
48
|
+
const balance = await this.getBalance(userId);
|
|
49
|
+
if (balance < costUsd)
|
|
50
|
+
return false;
|
|
51
|
+
// Optimistic: update cache immediately to prevent concurrent over-deduction
|
|
52
|
+
const newBalance = Math.max(0, balance - costUsd);
|
|
53
|
+
this.setCache(userId, newBalance);
|
|
54
|
+
// Persist to DB asynchronously
|
|
55
|
+
this.db
|
|
56
|
+
.incrementCredit(userId, -costUsd)
|
|
57
|
+
.then((dbBalance) => {
|
|
58
|
+
// Reconcile: if DB says something different, trust DB
|
|
59
|
+
this.setCache(userId, Math.max(0, dbBalance));
|
|
60
|
+
})
|
|
61
|
+
.catch((err) => {
|
|
62
|
+
console.error('[CreditStore] Failed to persist deduction', { userId, costUsd }, err);
|
|
63
|
+
// Roll back optimistic decrement on failure
|
|
64
|
+
this.setCache(userId, balance);
|
|
65
|
+
});
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
/** Invalidate cached balance for a user (e.g. after external grant). */
|
|
69
|
+
invalidate(userId) {
|
|
70
|
+
this.cache.delete(userId);
|
|
71
|
+
}
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Private
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
setCache(userId, balanceUsd) {
|
|
76
|
+
// Simple LRU eviction: if at capacity and key is new, evict oldest entry
|
|
77
|
+
if (!this.cache.has(userId) && this.cache.size >= this.maxSize) {
|
|
78
|
+
const oldest = this.cache.keys().next().value;
|
|
79
|
+
if (oldest !== undefined)
|
|
80
|
+
this.cache.delete(oldest);
|
|
81
|
+
}
|
|
82
|
+
this.cache.set(userId, { balanceUsd, fetchedAt: Date.now() });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* MongoDB-backed CreditDB implementation.
|
|
87
|
+
* Uses findOneAndUpdate-style atomic increment via a helper that performs
|
|
88
|
+
* a read-modify-write with the document's balanceUsd field.
|
|
89
|
+
*/
|
|
90
|
+
export class MongoCreditDB {
|
|
91
|
+
db;
|
|
92
|
+
constructor(db) {
|
|
93
|
+
this.db = db;
|
|
94
|
+
}
|
|
95
|
+
async incrementCredit(userId, deltaUsd) {
|
|
96
|
+
const existing = await this.db.getDoc(userId);
|
|
97
|
+
const current = existing?.balanceUsd ?? 0;
|
|
98
|
+
const newBalance = Math.max(0, current + deltaUsd);
|
|
99
|
+
const now = Date.now();
|
|
100
|
+
await this.db.setDoc(userId, {
|
|
101
|
+
id: userId,
|
|
102
|
+
balanceUsd: newBalance,
|
|
103
|
+
updated: now,
|
|
104
|
+
});
|
|
105
|
+
return newBalance;
|
|
106
|
+
}
|
|
107
|
+
async getCredit(userId) {
|
|
108
|
+
const doc = await this.db.getDoc(userId);
|
|
109
|
+
return doc?.balanceUsd ?? 0;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=CreditStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CreditStore.js","sourceRoot":"","sources":["../../../src/server/billing/CreditStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { BillingLedger } from './BillingLedger.js';
|
|
2
|
+
import type { CreditStore } from './CreditStore.js';
|
|
3
|
+
import type { BillingConfig, ChargeParams, GlobalThresholdCallback, UserThresholdCallback } from './types.js';
|
|
4
|
+
import type { UserLimitCache } from './UserLimitCache.js';
|
|
5
|
+
/**
|
|
6
|
+
* Checks all billing limits and fires threshold callbacks.
|
|
7
|
+
*
|
|
8
|
+
* Global and provider hourly limits use a synchronous in-memory rolling
|
|
9
|
+
* accumulator — race-free within a single Node.js process.
|
|
10
|
+
*
|
|
11
|
+
* Per-user hourly/daily/weekly limits perform async DB queries via BillingLedger.
|
|
12
|
+
*
|
|
13
|
+
* Known limitations (documented):
|
|
14
|
+
* - Multi-server: each server's accumulator is independent; limits are eventually
|
|
15
|
+
* consistent up to the reconciliation interval.
|
|
16
|
+
* - TOCTOU for per-user async checks: concurrent charges for the same userId can
|
|
17
|
+
* both pass under very high concurrent load. Mitigable with per-userId mutex (v2).
|
|
18
|
+
*/
|
|
19
|
+
export declare class LimitEnforcer {
|
|
20
|
+
private readonly ledger;
|
|
21
|
+
private readonly userLimits;
|
|
22
|
+
private readonly config;
|
|
23
|
+
private readonly credits;
|
|
24
|
+
private readonly accumulators;
|
|
25
|
+
private readonly firedThresholds;
|
|
26
|
+
private userThresholdCb;
|
|
27
|
+
private globalThresholdCb;
|
|
28
|
+
constructor(ledger: BillingLedger, userLimits: UserLimitCache, config: BillingConfig, credits?: CreditStore | null);
|
|
29
|
+
setUserThresholdCallback(fn: UserThresholdCallback): void;
|
|
30
|
+
setGlobalThresholdCallback(fn: GlobalThresholdCallback): void;
|
|
31
|
+
/**
|
|
32
|
+
* Load current-window spend from DB to warm the in-memory accumulators.
|
|
33
|
+
* Called by BillingGateway.start() before serving traffic.
|
|
34
|
+
*/
|
|
35
|
+
init(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Check all applicable limits for a charge.
|
|
38
|
+
*
|
|
39
|
+
* Check order (observable — documented):
|
|
40
|
+
* 1. global-hourly
|
|
41
|
+
* 2. provider-hourly
|
|
42
|
+
* 3. user-hourly
|
|
43
|
+
* 4. user-daily
|
|
44
|
+
* 5. user-weekly
|
|
45
|
+
*
|
|
46
|
+
* Throws BillingLimitError on first violation.
|
|
47
|
+
* Throws BillingValidationError on invalid input.
|
|
48
|
+
*/
|
|
49
|
+
check(params: ChargeParams): Promise<void>;
|
|
50
|
+
private addToAccumulator;
|
|
51
|
+
/** Remove the most recent entry that matches cost+ts (rollback of optimistic add). */
|
|
52
|
+
private removeFromAccumulator;
|
|
53
|
+
private pruneAccumulator;
|
|
54
|
+
private sumAccumulator;
|
|
55
|
+
private checkUserLimits;
|
|
56
|
+
/**
|
|
57
|
+
* If a CreditStore is configured and the user has sufficient balance, deduct and return.
|
|
58
|
+
* Otherwise throw the original limit error.
|
|
59
|
+
*/
|
|
60
|
+
private tryCreditsOrThrow;
|
|
61
|
+
/**
|
|
62
|
+
* Fire the global/provider threshold callback if the charge crosses the threshold.
|
|
63
|
+
* Fires at most once per window per threshold ratio (deduplicated by firedThresholds map).
|
|
64
|
+
* Window reset is detected when the accumulator for that key becomes empty after pruning.
|
|
65
|
+
*/
|
|
66
|
+
private maybeFireGlobalThreshold;
|
|
67
|
+
/**
|
|
68
|
+
* Fire the user threshold callback if the charge crosses a per-user window threshold.
|
|
69
|
+
* User threshold state is tracked per (userId, limitType) key.
|
|
70
|
+
*/
|
|
71
|
+
private maybeFireUserThreshold;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=LimitEnforcer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LimitEnforcer.d.ts","sourceRoot":"","sources":["../../../src/server/billing/LimitEnforcer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,uBAAuB,EAEvB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAW1D;;;;;;;;;;;;;GAaG;AACH,qBAAa,aAAa;IAQtB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAV1B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyC;IAEtE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkC;IAClE,OAAO,CAAC,eAAe,CAAsC;IAC7D,OAAO,CAAC,iBAAiB,CAAwC;gBAG9C,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,cAAc,EAC1B,MAAM,EAAE,aAAa,EACrB,OAAO,GAAE,WAAW,GAAG,IAAW;IAGrD,wBAAwB,CAAC,EAAE,EAAE,qBAAqB,GAAG,IAAI;IAIzD,0BAA0B,CAAC,EAAE,EAAE,uBAAuB,GAAG,IAAI;IAI7D;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB3B;;;;;;;;;;;;OAYG;IACG,KAAK,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAsFhD,OAAO,CAAC,gBAAgB;IAMxB,sFAAsF;IACtF,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,cAAc;YAUR,eAAe;IAuE7B;;;OAGG;YACW,iBAAiB;IAgB/B;;;;OAIG;IACH,OAAO,CAAC,wBAAwB;IAiChC;;;OAGG;IACH,OAAO,CAAC,sBAAsB;CAiC/B"}
|