@brokr/sdk 1.0.0 → 2.0.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/dist/account.js +34 -0
- package/dist/account.mjs +7 -0
- package/dist/auth.js +628 -114
- package/dist/auth.mjs +611 -111
- package/dist/chat.js +34 -0
- package/dist/chat.mjs +7 -0
- package/dist/events.js +64 -0
- package/dist/events.mjs +37 -0
- package/dist/feature.js +6304 -0
- package/dist/feature.mjs +6278 -0
- package/dist/files.js +428 -0
- package/dist/files.mjs +408 -0
- package/dist/index.d.ts +18 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4069 -454
- package/dist/index.mjs +4040 -448
- package/dist/logs.js +148 -0
- package/dist/logs.mjs +124 -0
- package/dist/management.js +14 -13
- package/dist/management.mjs +14 -13
- package/dist/next.js +2725 -0
- package/dist/next.mjs +2710 -0
- package/dist/notifications.js +140 -0
- package/dist/notifications.mjs +110 -0
- package/dist/payments.js +32 -0
- package/dist/payments.mjs +7 -0
- package/dist/react-notifications.js +286 -0
- package/dist/react-notifications.mjs +254 -0
- package/dist/react-styles.js +2718 -0
- package/dist/react-styles.mjs +2682 -0
- package/dist/react-theme.js +4194 -0
- package/dist/react-theme.mjs +4170 -0
- package/dist/react.js +8512 -209
- package/dist/react.mjs +8488 -179
- package/dist/runtime.js +2113 -385
- package/dist/runtime.mjs +2085 -397
- package/dist/src/account/config.d.ts +42 -0
- package/dist/src/account/config.d.ts.map +1 -0
- package/dist/src/account/index.d.ts +3 -0
- package/dist/src/account/index.d.ts.map +1 -0
- package/dist/src/ai/client.d.ts +58 -0
- package/dist/src/ai/client.d.ts.map +1 -0
- package/dist/src/ai/conversation-title.d.ts +13 -0
- package/dist/src/ai/conversation-title.d.ts.map +1 -0
- package/dist/src/ai/types.d.ts +81 -0
- package/dist/src/ai/types.d.ts.map +1 -0
- package/dist/src/auth.d.ts +133 -20
- package/dist/src/auth.d.ts.map +1 -1
- package/dist/src/chat/config.d.ts +61 -0
- package/dist/src/chat/config.d.ts.map +1 -0
- package/dist/src/chat/index.d.ts +3 -0
- package/dist/src/chat/index.d.ts.map +1 -0
- package/dist/src/chat/sse-parser.d.ts +44 -0
- package/dist/src/chat/sse-parser.d.ts.map +1 -0
- package/dist/src/dev-console.d.ts +18 -0
- package/dist/src/dev-console.d.ts.map +1 -0
- package/dist/src/email/client.d.ts +33 -0
- package/dist/src/email/client.d.ts.map +1 -0
- package/dist/src/email/templates.d.ts +15 -0
- package/dist/src/email/templates.d.ts.map +1 -0
- package/dist/src/email/types.d.ts +35 -0
- package/dist/src/email/types.d.ts.map +1 -0
- package/dist/src/env-detect.d.ts +25 -0
- package/dist/src/env-detect.d.ts.map +1 -0
- package/dist/src/errors.d.ts +53 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/events/client.d.ts +25 -0
- package/dist/src/events/client.d.ts.map +1 -0
- package/dist/src/events/index.d.ts +9 -0
- package/dist/src/events/index.d.ts.map +1 -0
- package/dist/src/events/types.d.ts +10 -0
- package/dist/src/events/types.d.ts.map +1 -0
- package/dist/src/feature/canonical.d.ts +33 -0
- package/dist/src/feature/canonical.d.ts.map +1 -0
- package/dist/src/feature/create-feature.d.ts +33 -0
- package/dist/src/feature/create-feature.d.ts.map +1 -0
- package/dist/src/feature/db.d.ts +16 -0
- package/dist/src/feature/db.d.ts.map +1 -0
- package/dist/src/feature/handlers.d.ts +28 -0
- package/dist/src/feature/handlers.d.ts.map +1 -0
- package/dist/src/feature/index.d.ts +21 -0
- package/dist/src/feature/index.d.ts.map +1 -0
- package/dist/src/feature/manifest.d.ts +173 -0
- package/dist/src/feature/manifest.d.ts.map +1 -0
- package/dist/src/feature/mapping.d.ts +18 -0
- package/dist/src/feature/mapping.d.ts.map +1 -0
- package/dist/src/feature/runtime.d.ts +45 -0
- package/dist/src/feature/runtime.d.ts.map +1 -0
- package/dist/src/feature/types.d.ts +65 -0
- package/dist/src/feature/types.d.ts.map +1 -0
- package/dist/src/files/client.d.ts +28 -0
- package/dist/src/files/client.d.ts.map +1 -0
- package/dist/src/files/types.d.ts +28 -0
- package/dist/src/files/types.d.ts.map +1 -0
- package/dist/src/fix-registry.d.ts +8 -0
- package/dist/src/fix-registry.d.ts.map +1 -0
- package/dist/src/gateway.d.ts +32 -0
- package/dist/src/gateway.d.ts.map +1 -0
- package/dist/src/logs/capture.d.ts +56 -0
- package/dist/src/logs/capture.d.ts.map +1 -0
- package/dist/src/logs/index.d.ts +2 -0
- package/dist/src/logs/index.d.ts.map +1 -0
- package/dist/src/management.d.ts +1 -1
- package/dist/src/management.d.ts.map +1 -1
- package/dist/src/models.d.ts +32 -0
- package/dist/src/models.d.ts.map +1 -0
- package/dist/src/next/auth.d.ts +54 -0
- package/dist/src/next/auth.d.ts.map +1 -0
- package/dist/src/next/chat.d.ts +31 -0
- package/dist/src/next/chat.d.ts.map +1 -0
- package/dist/src/next/index.d.ts +14 -0
- package/dist/src/next/index.d.ts.map +1 -0
- package/dist/src/next/notifications.d.ts +67 -0
- package/dist/src/next/notifications.d.ts.map +1 -0
- package/dist/src/notifications/built-ins.d.ts +9 -0
- package/dist/src/notifications/built-ins.d.ts.map +1 -0
- package/dist/src/notifications/client.d.ts +38 -0
- package/dist/src/notifications/client.d.ts.map +1 -0
- package/dist/src/notifications/config.d.ts +71 -0
- package/dist/src/notifications/config.d.ts.map +1 -0
- package/dist/src/notifications/index.d.ts +6 -0
- package/dist/src/notifications/index.d.ts.map +1 -0
- package/dist/src/notifications/registry.d.ts +67 -0
- package/dist/src/notifications/registry.d.ts.map +1 -0
- package/dist/src/notifications/types.d.ts +48 -0
- package/dist/src/notifications/types.d.ts.map +1 -0
- package/dist/src/payments/client.d.ts +64 -0
- package/dist/src/payments/client.d.ts.map +1 -0
- package/dist/src/payments/config.d.ts +46 -0
- package/dist/src/payments/config.d.ts.map +1 -0
- package/dist/src/payments/entitlements.d.ts +48 -0
- package/dist/src/payments/entitlements.d.ts.map +1 -0
- package/dist/src/payments/types.d.ts +135 -0
- package/dist/src/payments/types.d.ts.map +1 -0
- package/dist/src/react/BrokrErrorBoundary.d.ts +23 -0
- package/dist/src/react/BrokrErrorBoundary.d.ts.map +1 -0
- package/dist/src/react/account/AccountPanel.d.ts +12 -0
- package/dist/src/react/account/AccountPanel.d.ts.map +1 -0
- package/dist/src/react/account/Avatar.d.ts +11 -0
- package/dist/src/react/account/Avatar.d.ts.map +1 -0
- package/dist/src/react/account/ProfilePhotoButton.d.ts +7 -0
- package/dist/src/react/account/ProfilePhotoButton.d.ts.map +1 -0
- package/dist/src/react/account/UserButton.d.ts +7 -0
- package/dist/src/react/account/UserButton.d.ts.map +1 -0
- package/dist/src/react/auth-pages/AuthPageShell.d.ts +9 -0
- package/dist/src/react/auth-pages/AuthPageShell.d.ts.map +1 -0
- package/dist/src/react/auth-pages/SignInPage.d.ts +9 -0
- package/dist/src/react/auth-pages/SignInPage.d.ts.map +1 -0
- package/dist/src/react/auth-pages/SignUpPage.d.ts +8 -0
- package/dist/src/react/auth-pages/SignUpPage.d.ts.map +1 -0
- package/dist/src/react/auth.d.ts +1 -49
- package/dist/src/react/auth.d.ts.map +1 -1
- package/dist/src/react/chat/AIChat.d.ts +4 -0
- package/dist/src/react/chat/AIChat.d.ts.map +1 -0
- package/dist/src/react/chat/ChatContext.d.ts +76 -0
- package/dist/src/react/chat/ChatContext.d.ts.map +1 -0
- package/dist/src/react/chat/ChatInput.d.ts +3 -0
- package/dist/src/react/chat/ChatInput.d.ts.map +1 -0
- package/dist/src/react/chat/MarkdownRenderer.d.ts +5 -0
- package/dist/src/react/chat/MarkdownRenderer.d.ts.map +1 -0
- package/dist/src/react/chat/MessageBubble.d.ts +14 -0
- package/dist/src/react/chat/MessageBubble.d.ts.map +1 -0
- package/dist/src/react/chat/MessagePane.d.ts +10 -0
- package/dist/src/react/chat/MessagePane.d.ts.map +1 -0
- package/dist/src/react/chat/ModelSelector.d.ts +13 -0
- package/dist/src/react/chat/ModelSelector.d.ts.map +1 -0
- package/dist/src/react/chat/ThreadSidebar.d.ts +3 -0
- package/dist/src/react/chat/ThreadSidebar.d.ts.map +1 -0
- package/dist/src/react/chat/index.d.ts +5 -0
- package/dist/src/react/chat/index.d.ts.map +1 -0
- package/dist/src/react/chat/token-limit.d.ts +14 -0
- package/dist/src/react/chat/token-limit.d.ts.map +1 -0
- package/dist/src/react/chat/types.d.ts +65 -0
- package/dist/src/react/chat/types.d.ts.map +1 -0
- package/dist/src/react/chat/useChat.d.ts +57 -0
- package/dist/src/react/chat/useChat.d.ts.map +1 -0
- package/dist/src/react/composites/FabAI.d.ts +15 -0
- package/dist/src/react/composites/FabAI.d.ts.map +1 -0
- package/dist/src/react/composites/FeedbackWidget.d.ts +10 -0
- package/dist/src/react/composites/FeedbackWidget.d.ts.map +1 -0
- package/dist/src/react/composites/SmartUpload.d.ts +12 -0
- package/dist/src/react/composites/SmartUpload.d.ts.map +1 -0
- package/dist/src/react/config.d.ts +23 -0
- package/dist/src/react/config.d.ts.map +1 -0
- package/dist/src/react/context.d.ts +4 -0
- package/dist/src/react/context.d.ts.map +1 -0
- package/dist/src/react/css/account.d.ts +2 -0
- package/dist/src/react/css/account.d.ts.map +1 -0
- package/dist/src/react/css/animations.d.ts +2 -0
- package/dist/src/react/css/animations.d.ts.map +1 -0
- package/dist/src/react/css/auth.d.ts +2 -0
- package/dist/src/react/css/auth.d.ts.map +1 -0
- package/dist/src/react/css/chat-extras.d.ts +2 -0
- package/dist/src/react/css/chat-extras.d.ts.map +1 -0
- package/dist/src/react/css/chat.d.ts +2 -0
- package/dist/src/react/css/chat.d.ts.map +1 -0
- package/dist/src/react/css/composites.d.ts +2 -0
- package/dist/src/react/css/composites.d.ts.map +1 -0
- package/dist/src/react/css/gates.d.ts +2 -0
- package/dist/src/react/css/gates.d.ts.map +1 -0
- package/dist/src/react/css/index.d.ts +3 -0
- package/dist/src/react/css/index.d.ts.map +1 -0
- package/dist/src/react/css/markdown.d.ts +2 -0
- package/dist/src/react/css/markdown.d.ts.map +1 -0
- package/dist/src/react/css/notifications.d.ts +2 -0
- package/dist/src/react/css/notifications.d.ts.map +1 -0
- package/dist/src/react/css/primitives.d.ts +2 -0
- package/dist/src/react/css/primitives.d.ts.map +1 -0
- package/dist/src/react/css/reset.d.ts +2 -0
- package/dist/src/react/css/reset.d.ts.map +1 -0
- package/dist/src/react/css/responsive.d.ts +2 -0
- package/dist/src/react/css/responsive.d.ts.map +1 -0
- package/dist/src/react/css/skeleton.d.ts +2 -0
- package/dist/src/react/css/skeleton.d.ts.map +1 -0
- package/dist/src/react/css/tokens.d.ts +2 -0
- package/dist/src/react/css/tokens.d.ts.map +1 -0
- package/dist/src/react/gates/AuthWall.d.ts +7 -0
- package/dist/src/react/gates/AuthWall.d.ts.map +1 -0
- package/dist/src/react/gates/BillingBoundary.d.ts +4 -0
- package/dist/src/react/gates/BillingBoundary.d.ts.map +1 -0
- package/dist/src/react/gates/Gate.d.ts +9 -0
- package/dist/src/react/gates/Gate.d.ts.map +1 -0
- package/dist/src/react/gates/RequirePlan.d.ts +4 -0
- package/dist/src/react/gates/RequirePlan.d.ts.map +1 -0
- package/dist/src/react/gates/RequireUser.d.ts +4 -0
- package/dist/src/react/gates/RequireUser.d.ts.map +1 -0
- package/dist/src/react/gates/UsageGate.d.ts +4 -0
- package/dist/src/react/gates/UsageGate.d.ts.map +1 -0
- package/dist/src/react/helpers.d.ts +7 -0
- package/dist/src/react/helpers.d.ts.map +1 -0
- package/dist/src/react/hooks/use-theme.d.ts +15 -0
- package/dist/src/react/hooks/use-theme.d.ts.map +1 -0
- package/dist/src/react/hooks/use-user.d.ts +12 -0
- package/dist/src/react/hooks/use-user.d.ts.map +1 -0
- package/dist/src/react/icons.d.ts +26 -0
- package/dist/src/react/icons.d.ts.map +1 -0
- package/dist/src/react/index.d.ts +48 -0
- package/dist/src/react/index.d.ts.map +1 -0
- package/dist/src/react/notifications/NotificationBell.d.ts +7 -0
- package/dist/src/react/notifications/NotificationBell.d.ts.map +1 -0
- package/dist/src/react/notifications/NotificationList.d.ts +7 -0
- package/dist/src/react/notifications/NotificationList.d.ts.map +1 -0
- package/dist/src/react/notifications/Toast.d.ts +13 -0
- package/dist/src/react/notifications/Toast.d.ts.map +1 -0
- package/dist/src/react/notifications/index.d.ts +8 -0
- package/dist/src/react/notifications/index.d.ts.map +1 -0
- package/dist/src/react/notifications/provider.d.ts +14 -0
- package/dist/src/react/notifications/provider.d.ts.map +1 -0
- package/dist/src/react/notifications/use-notifications.d.ts +24 -0
- package/dist/src/react/notifications/use-notifications.d.ts.map +1 -0
- package/dist/src/react/payments/AutoReloadToggle.d.ts +6 -0
- package/dist/src/react/payments/AutoReloadToggle.d.ts.map +1 -0
- package/dist/src/react/payments/Balance.d.ts +8 -0
- package/dist/src/react/payments/Balance.d.ts.map +1 -0
- package/dist/src/react/payments/CancelSubscription.d.ts +6 -0
- package/dist/src/react/payments/CancelSubscription.d.ts.map +1 -0
- package/dist/src/react/payments/CheckoutButton.d.ts +7 -0
- package/dist/src/react/payments/CheckoutButton.d.ts.map +1 -0
- package/dist/src/react/payments/CustomerPortalButton.d.ts +6 -0
- package/dist/src/react/payments/CustomerPortalButton.d.ts.map +1 -0
- package/dist/src/react/payments/FeatureMeter.d.ts +6 -0
- package/dist/src/react/payments/FeatureMeter.d.ts.map +1 -0
- package/dist/src/react/payments/Plans.d.ts +8 -0
- package/dist/src/react/payments/Plans.d.ts.map +1 -0
- package/dist/src/react/payments/TopUpButton.d.ts +8 -0
- package/dist/src/react/payments/TopUpButton.d.ts.map +1 -0
- package/dist/src/react/payments/UpdateBilling.d.ts +3 -0
- package/dist/src/react/payments/UpdateBilling.d.ts.map +1 -0
- package/dist/src/react/payments/UpgradePrompt.d.ts +8 -0
- package/dist/src/react/payments/UpgradePrompt.d.ts.map +1 -0
- package/dist/src/react/primitives/Skeleton.d.ts +15 -0
- package/dist/src/react/primitives/Skeleton.d.ts.map +1 -0
- package/dist/src/react/provider.d.ts +21 -0
- package/dist/src/react/provider.d.ts.map +1 -0
- package/dist/src/react/request.d.ts +2 -0
- package/dist/src/react/request.d.ts.map +1 -0
- package/dist/src/react/styles-entry.d.ts +4 -0
- package/dist/src/react/styles-entry.d.ts.map +1 -0
- package/dist/src/react/styles.d.ts +2 -0
- package/dist/src/react/styles.d.ts.map +1 -0
- package/dist/src/react/theme-entry.d.ts +3 -0
- package/dist/src/react/theme-entry.d.ts.map +1 -0
- package/dist/src/react/theme.d.ts +6 -0
- package/dist/src/react/theme.d.ts.map +1 -0
- package/dist/src/react/types.d.ts +191 -0
- package/dist/src/react/types.d.ts.map +1 -0
- package/dist/src/react/use-brokr-theme.d.ts +6 -0
- package/dist/src/react/use-brokr-theme.d.ts.map +1 -0
- package/dist/src/runtime.d.ts +69 -180
- package/dist/src/runtime.d.ts.map +1 -1
- package/dist/src/storage/client.d.ts +113 -0
- package/dist/src/storage/client.d.ts.map +1 -0
- package/dist/src/storage/types.d.ts +60 -0
- package/dist/src/storage/types.d.ts.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +70 -9
package/dist/auth.mjs
CHANGED
|
@@ -1,26 +1,616 @@
|
|
|
1
|
-
|
|
2
|
-
var
|
|
3
|
-
|
|
1
|
+
// src/fix-registry.ts
|
|
2
|
+
var FIX_REGISTRY = {
|
|
3
|
+
AUTH_TOKEN_INVALID: [
|
|
4
|
+
"\u2192 Run `brokr env pull --stack <name>` to refresh your token",
|
|
5
|
+
"\u2192 Or run `brokr link account` to re-authenticate"
|
|
6
|
+
].join("\n"),
|
|
7
|
+
AUTH_SESSION_EXPIRED: [
|
|
8
|
+
"\u2192 Run `brokr link account` to re-authenticate"
|
|
9
|
+
].join("\n"),
|
|
10
|
+
BROKR_TOKEN_MISSING: [
|
|
11
|
+
"\u2192 Run `brokr env pull --stack <name>` to sync your environment",
|
|
12
|
+
"\u2192 Or add BROKR_TOKEN to your .env.local manually"
|
|
13
|
+
].join("\n"),
|
|
14
|
+
DATABASE_PROVISION_FAILED: [
|
|
15
|
+
"\u2192 Run `brokr status <stack>` to check current state",
|
|
16
|
+
"\u2192 Run `brokr retry <stack>` to re-trigger provisioning"
|
|
17
|
+
].join("\n"),
|
|
18
|
+
DATABASE_QUOTA_REACHED: [
|
|
19
|
+
"\u2192 Upgrade your plan or delete unused databases",
|
|
20
|
+
"\u2192 Check usage at brokr.sh/billing"
|
|
21
|
+
].join("\n"),
|
|
22
|
+
DATABASE_HEALTH_TIMEOUT: [
|
|
23
|
+
"\u2192 Run `brokr status <stack>` to check database state",
|
|
24
|
+
"\u2192 If persistent, check provider status page"
|
|
25
|
+
].join("\n"),
|
|
26
|
+
DEPLOYMENT_FAILED: [
|
|
27
|
+
"\u2192 Run `brokr logs --stack <name>` to see build logs",
|
|
28
|
+
"\u2192 Fix the build error and run `brokr deploy`"
|
|
29
|
+
].join("\n"),
|
|
30
|
+
DEPLOYMENT_RATE_LIMITED: [
|
|
31
|
+
"\u2192 Wait a minute and retry",
|
|
32
|
+
"\u2192 Vercel rate limits apply per project"
|
|
33
|
+
].join("\n"),
|
|
34
|
+
REPO_ALREADY_EXISTS: [
|
|
35
|
+
"\u2192 Use a different stack name",
|
|
36
|
+
"\u2192 Or run `brokr status <stack>` to check the existing stack"
|
|
37
|
+
].join("\n"),
|
|
38
|
+
REPO_CREATE_FAILED: [
|
|
39
|
+
"\u2192 Check your GitHub connection: `brokr link account`",
|
|
40
|
+
"\u2192 Ensure the Brokr GitHub App is installed"
|
|
41
|
+
].join("\n"),
|
|
42
|
+
EMAIL_DOMAIN_FAILED: [
|
|
43
|
+
"\u2192 Check DNS records are propagated",
|
|
44
|
+
"\u2192 Run `brokr status <stack>` for details"
|
|
45
|
+
].join("\n"),
|
|
46
|
+
DNS_RECORD_FAILED: [
|
|
47
|
+
"\u2192 Check Cloudflare dashboard for zone status",
|
|
48
|
+
"\u2192 Retry with `brokr retry <stack>`"
|
|
49
|
+
].join("\n"),
|
|
50
|
+
INSUFFICIENT_CREDITS: [
|
|
51
|
+
"\u2192 Top up credits at brokr.sh/billing",
|
|
52
|
+
"\u2192 Or run `brokr billing topup`"
|
|
53
|
+
].join("\n"),
|
|
54
|
+
AI_BUDGET_EXCEEDED: [
|
|
55
|
+
"\u2192 Top up credits at brokr.sh/billing",
|
|
56
|
+
"\u2192 Or run `brokr billing topup`"
|
|
57
|
+
].join("\n"),
|
|
58
|
+
AI_PROVIDER_UNAVAILABLE: [
|
|
59
|
+
"\u2192 The AI provider is temporarily down",
|
|
60
|
+
"\u2192 Retry in a few seconds \u2014 this is usually transient"
|
|
61
|
+
].join("\n"),
|
|
62
|
+
AI_MODEL_NOT_FOUND: [
|
|
63
|
+
"\u2192 Check the model name in your configuration",
|
|
64
|
+
"\u2192 See available models at brokr.sh/docs/ai-models"
|
|
65
|
+
].join("\n"),
|
|
66
|
+
ENV_VAR_MISSING: [
|
|
67
|
+
"\u2192 Run `brokr env pull --stack <name>` to sync environment",
|
|
68
|
+
"\u2192 Check required vars in your template README"
|
|
69
|
+
].join("\n"),
|
|
70
|
+
STACK_LIMIT_REACHED: [
|
|
71
|
+
"\u2192 Delete unused stacks with `brokr delete <stack>`",
|
|
72
|
+
"\u2192 Or upgrade your plan at brokr.sh/billing"
|
|
73
|
+
].join("\n"),
|
|
74
|
+
STACK_NOT_FOUND: [
|
|
75
|
+
"\u2192 Check the stack name: `brokr list`",
|
|
76
|
+
"\u2192 Create a new stack: `brokr create --name <name>`"
|
|
77
|
+
].join("\n"),
|
|
78
|
+
RATE_LIMITED: [
|
|
79
|
+
"\u2192 Wait a moment and retry",
|
|
80
|
+
"\u2192 If persistent, check brokr.sh/status"
|
|
81
|
+
].join("\n"),
|
|
82
|
+
GATEWAY_AUTH_FAILED: [
|
|
83
|
+
"\u2192 Your BROKR_TOKEN may be expired",
|
|
84
|
+
"\u2192 Run `brokr env pull --stack <name>` to refresh"
|
|
85
|
+
].join("\n"),
|
|
86
|
+
BROKR_TOKEN_INVALID: [
|
|
87
|
+
"\u2192 Your BROKR_TOKEN has expired or been revoked",
|
|
88
|
+
"\u2192 Run `brokr env pull --stack <name>` to get a fresh token"
|
|
89
|
+
].join("\n"),
|
|
90
|
+
AI_STREAM_ERROR: [
|
|
91
|
+
"\u2192 The AI stream was interrupted",
|
|
92
|
+
"\u2192 Retry the request \u2014 this is usually transient"
|
|
93
|
+
].join("\n"),
|
|
94
|
+
EMAIL_SEND_FAILED: [
|
|
95
|
+
"\u2192 Email delivery failed",
|
|
96
|
+
"\u2192 Check that your domain DNS is verified: `brokr status <stack>`"
|
|
97
|
+
].join("\n"),
|
|
98
|
+
EMAIL_NOT_CONFIGURED: [
|
|
99
|
+
"\u2192 Email is not set up for this stack",
|
|
100
|
+
"\u2192 Add email capability: `brokr add email --stack <name>`"
|
|
101
|
+
].join("\n"),
|
|
102
|
+
EMAIL_INVALID_FROM: [
|
|
103
|
+
"\u2192 The from address must match your verified domain",
|
|
104
|
+
"\u2192 Check your domain: `brokr status <stack>`"
|
|
105
|
+
].join("\n"),
|
|
106
|
+
STORAGE_UPLOAD_FAILED: [
|
|
107
|
+
"\u2192 File upload failed",
|
|
108
|
+
"\u2192 Check file size limits and retry"
|
|
109
|
+
].join("\n"),
|
|
110
|
+
STORAGE_NOT_FOUND: [
|
|
111
|
+
"\u2192 The requested file does not exist",
|
|
112
|
+
"\u2192 Check the key and try again"
|
|
113
|
+
].join("\n"),
|
|
114
|
+
PAYMENTS_NOT_CONFIGURED: [
|
|
115
|
+
"\u2192 Payments are not set up for this stack",
|
|
116
|
+
"\u2192 Run `brokr payments sync` to configure Stripe"
|
|
117
|
+
].join("\n"),
|
|
118
|
+
PAYMENTS_FAILED: [
|
|
119
|
+
"\u2192 Payment processing failed",
|
|
120
|
+
"\u2192 Check your Stripe dashboard for details"
|
|
121
|
+
].join("\n"),
|
|
122
|
+
GATEWAY_INTERNAL: [
|
|
123
|
+
"\u2192 The Brokr gateway encountered an internal error",
|
|
124
|
+
"\u2192 Check brokr.sh/status for service health"
|
|
125
|
+
].join("\n"),
|
|
126
|
+
UPSTREAM_ERROR: [
|
|
127
|
+
"\u2192 An upstream service is temporarily unavailable",
|
|
128
|
+
"\u2192 Retry in a few seconds \u2014 this is usually transient"
|
|
129
|
+
].join("\n"),
|
|
130
|
+
NETWORK_ERROR: [
|
|
131
|
+
"\u2192 Could not reach the Brokr gateway",
|
|
132
|
+
"\u2192 Check your internet connection",
|
|
133
|
+
"\u2192 Check brokr.sh/status for service health"
|
|
134
|
+
].join("\n"),
|
|
135
|
+
TIMEOUT: [
|
|
136
|
+
"\u2192 Request timed out",
|
|
137
|
+
"\u2192 Retry \u2014 if persistent, check brokr.sh/status"
|
|
138
|
+
].join("\n")
|
|
4
139
|
};
|
|
5
140
|
|
|
6
|
-
// src/
|
|
7
|
-
var BrokrError
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
141
|
+
// src/errors.ts
|
|
142
|
+
var BrokrError = class extends Error {
|
|
143
|
+
constructor(message, code, capability, retryable = false, errorCode, requestId, hint) {
|
|
144
|
+
super(message);
|
|
145
|
+
this.code = code;
|
|
146
|
+
this.capability = capability;
|
|
147
|
+
this.retryable = retryable;
|
|
148
|
+
this.errorCode = errorCode;
|
|
149
|
+
this.requestId = requestId;
|
|
150
|
+
this.hint = hint;
|
|
151
|
+
this.name = "BrokrError";
|
|
152
|
+
}
|
|
153
|
+
/** Multi-line terminal fix block — looked up from error code. */
|
|
154
|
+
get fix() {
|
|
155
|
+
if (!this.errorCode) return void 0;
|
|
156
|
+
return FIX_REGISTRY[this.errorCode];
|
|
157
|
+
}
|
|
158
|
+
/** Documentation URL for this error code. */
|
|
159
|
+
get docsUrl() {
|
|
160
|
+
if (!this.errorCode) return void 0;
|
|
161
|
+
return `https://brokr.sh/errors/${this.errorCode.toLowerCase().replace(/_/g, "-")}`;
|
|
162
|
+
}
|
|
163
|
+
/** Safe message for end users — zero technical language. */
|
|
164
|
+
toUserMessage() {
|
|
165
|
+
switch (this.code) {
|
|
166
|
+
case "RATE_LIMITED":
|
|
167
|
+
return "Please wait a moment and try again.";
|
|
168
|
+
case "TIMEOUT":
|
|
169
|
+
return "The request took too long. Please try again.";
|
|
170
|
+
case "NETWORK_ERROR":
|
|
171
|
+
return "Connection issue. Check your internet and try again.";
|
|
172
|
+
case "BROKR_TOKEN_MISSING":
|
|
173
|
+
return "App is not connected to Brokr. Contact the developer.";
|
|
174
|
+
case "BROKR_TOKEN_INVALID":
|
|
175
|
+
return "Session expired. Please refresh.";
|
|
176
|
+
case "AI_BUDGET_EXCEEDED":
|
|
177
|
+
return "AI usage limit reached. The developer needs to add credits.";
|
|
178
|
+
case "AI_PROVIDER_UNAVAILABLE":
|
|
179
|
+
return "AI service is temporarily down. Please retry shortly.";
|
|
180
|
+
case "AI_PROVIDER_RATE_LIMITED":
|
|
181
|
+
return "Too many AI requests. Please wait a moment.";
|
|
182
|
+
case "INSUFFICIENT_CREDITS":
|
|
183
|
+
return "Not enough credits. Please top up your balance.";
|
|
184
|
+
case "STORAGE_UPLOAD_FAILED":
|
|
185
|
+
return "File upload failed. Please try again.";
|
|
186
|
+
case "STORAGE_NOT_FOUND":
|
|
187
|
+
return "File not found.";
|
|
188
|
+
case "EMAIL_SEND_FAILED":
|
|
189
|
+
return "Email could not be sent. Please try again.";
|
|
190
|
+
case "EMAIL_NOT_CONFIGURED":
|
|
191
|
+
return "Email is not set up for this app.";
|
|
192
|
+
case "PAYMENTS_NOT_CONFIGURED":
|
|
193
|
+
return "Payments are not configured. Contact the developer.";
|
|
194
|
+
case "PAYMENTS_FAILED":
|
|
195
|
+
return "Payment processing failed. Please try again.";
|
|
196
|
+
case "NOT_FOUND":
|
|
197
|
+
return "The requested resource was not found.";
|
|
198
|
+
case "VALIDATION_ERROR":
|
|
199
|
+
return "Invalid input. Please check your data and try again.";
|
|
200
|
+
default:
|
|
201
|
+
return "Something went wrong. We're looking into it.";
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
toString() {
|
|
205
|
+
return `${this.name} [${this.errorCode ?? this.code}]: ${this.message}`;
|
|
206
|
+
}
|
|
207
|
+
toJSON() {
|
|
208
|
+
return {
|
|
209
|
+
name: this.name,
|
|
210
|
+
code: this.code,
|
|
211
|
+
errorCode: this.errorCode,
|
|
212
|
+
message: this.message,
|
|
213
|
+
capability: this.capability,
|
|
214
|
+
retryable: this.retryable,
|
|
215
|
+
requestId: this.requestId,
|
|
216
|
+
hint: this.hint,
|
|
217
|
+
component: this.component
|
|
19
218
|
};
|
|
20
219
|
}
|
|
21
|
-
}
|
|
220
|
+
};
|
|
22
221
|
|
|
23
222
|
// src/auth.ts
|
|
223
|
+
function resolveAppUrl(appUrl) {
|
|
224
|
+
if (appUrl) return appUrl;
|
|
225
|
+
throw new BrokrError(
|
|
226
|
+
"[brokr] BROKR_AUTH_URL is not set. Auth may not be provisioned.",
|
|
227
|
+
"AUTH_NOT_CONFIGURED",
|
|
228
|
+
"auth"
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
function mapSessionUser(raw) {
|
|
232
|
+
return {
|
|
233
|
+
id: raw.id,
|
|
234
|
+
email: raw.email,
|
|
235
|
+
name: raw.name ?? null,
|
|
236
|
+
image: raw.image ?? null,
|
|
237
|
+
emailVerified: raw.emailVerified ? /* @__PURE__ */ new Date() : null
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function pluralizeResource(resource) {
|
|
241
|
+
if (resource.endsWith("s")) return resource;
|
|
242
|
+
if (resource.endsWith("y") && !/[aeiou]y$/i.test(resource)) return resource.slice(0, -1) + "ies";
|
|
243
|
+
return resource + "s";
|
|
244
|
+
}
|
|
245
|
+
function validateSqlIdentifier(name, label) {
|
|
246
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
|
|
247
|
+
throw new BrokrError(
|
|
248
|
+
`[brokr] Invalid ${label}: "${name}". Must be alphanumeric with underscores only.`,
|
|
249
|
+
"INVALID_IDENTIFIER",
|
|
250
|
+
"auth"
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
var _ownershipPool = null;
|
|
255
|
+
async function getOwnershipPool(dbUrl) {
|
|
256
|
+
let Pool;
|
|
257
|
+
try {
|
|
258
|
+
const mod = await import("@neondatabase/serverless");
|
|
259
|
+
Pool = mod.Pool;
|
|
260
|
+
} catch {
|
|
261
|
+
throw new BrokrError(
|
|
262
|
+
"[brokr] @neondatabase/serverless is required for ownership checks. Run: npm install @neondatabase/serverless",
|
|
263
|
+
"MISSING_DEPENDENCY",
|
|
264
|
+
"auth"
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
if (_ownershipPool && _ownershipPool.dbUrl === dbUrl) {
|
|
268
|
+
return _ownershipPool.pool;
|
|
269
|
+
}
|
|
270
|
+
const pool = new Pool({ connectionString: dbUrl, max: 3 });
|
|
271
|
+
_ownershipPool = { pool, dbUrl };
|
|
272
|
+
return pool;
|
|
273
|
+
}
|
|
274
|
+
async function authApiFetch(appUrl, path, options) {
|
|
275
|
+
const headers = { "Content-Type": "application/json" };
|
|
276
|
+
if (options?.cookies) headers.cookie = options.cookies;
|
|
277
|
+
if (typeof window === "undefined") {
|
|
278
|
+
headers.Origin = process.env.BETTER_AUTH_URL ?? process.env.NEXT_PUBLIC_APP_URL ?? process.env.APP_URL ?? process.env.BROKR_AUTH_URL ?? (process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : void 0) ?? appUrl;
|
|
279
|
+
}
|
|
280
|
+
const res = await fetch(`${appUrl}${path}`, {
|
|
281
|
+
method: options?.method ?? "GET",
|
|
282
|
+
headers,
|
|
283
|
+
body: options?.body ? JSON.stringify(options.body) : void 0
|
|
284
|
+
});
|
|
285
|
+
if (!res.ok) {
|
|
286
|
+
const data = await res.json().catch(() => ({}));
|
|
287
|
+
throw new BrokrError(
|
|
288
|
+
data.message ?? `[brokr] Auth API call failed (HTTP ${res.status})`,
|
|
289
|
+
"AUTH_API_FAILED",
|
|
290
|
+
"auth"
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
return res.json();
|
|
294
|
+
}
|
|
295
|
+
var BrokrAuthClient = class {
|
|
296
|
+
constructor(_token, _gatewayUrl, appUrl) {
|
|
297
|
+
this._appUrl = appUrl ?? (typeof process !== "undefined" ? process.env.BROKR_AUTH_URL : void 0);
|
|
298
|
+
}
|
|
299
|
+
// -------------------------------------------------------------------------
|
|
300
|
+
// Identity — read user/session from incoming request
|
|
301
|
+
// -------------------------------------------------------------------------
|
|
302
|
+
/**
|
|
303
|
+
* Get user from request headers. Returns null if not authenticated.
|
|
304
|
+
* Calls the app's own Better Auth API.
|
|
305
|
+
*/
|
|
306
|
+
async user(headers) {
|
|
307
|
+
const session = await this.session(headers);
|
|
308
|
+
return session?.user ?? null;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Get user from request headers. Throws 401 if not authenticated.
|
|
312
|
+
*/
|
|
313
|
+
async requireUser(headers) {
|
|
314
|
+
const u = await this.user(headers);
|
|
315
|
+
if (!u) {
|
|
316
|
+
throw new BrokrError("[brokr] Authentication required. The request has no valid session. Check that cookies are being forwarded.", "UNAUTHORIZED", "auth");
|
|
317
|
+
}
|
|
318
|
+
return u;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Get full session from request headers. Returns null if not authenticated.
|
|
322
|
+
*/
|
|
323
|
+
async session(headers) {
|
|
324
|
+
const appUrl = resolveAppUrl(this._appUrl);
|
|
325
|
+
const cookieHeader = headers.get("cookie") ?? "";
|
|
326
|
+
if (!cookieHeader) return null;
|
|
327
|
+
const res = await fetch(`${appUrl}/api/auth/get-session`, {
|
|
328
|
+
method: "GET",
|
|
329
|
+
headers: { cookie: cookieHeader }
|
|
330
|
+
});
|
|
331
|
+
if (!res.ok) return null;
|
|
332
|
+
const data = await res.json();
|
|
333
|
+
if (!data?.user || !data?.session) return null;
|
|
334
|
+
return {
|
|
335
|
+
user: mapSessionUser(data.user),
|
|
336
|
+
sessionId: data.session.id,
|
|
337
|
+
expiresAt: new Date(data.session.expiresAt)
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Get full session. Throws 401 if not authenticated.
|
|
342
|
+
*/
|
|
343
|
+
async requireSession(headers) {
|
|
344
|
+
const s = await this.session(headers);
|
|
345
|
+
if (!s) {
|
|
346
|
+
throw new BrokrError("[brokr] Authentication required. The request has no valid session. Check that cookies are being forwarded.", "UNAUTHORIZED", "auth");
|
|
347
|
+
}
|
|
348
|
+
return s;
|
|
349
|
+
}
|
|
350
|
+
// -------------------------------------------------------------------------
|
|
351
|
+
// Legacy aliases (backward compat)
|
|
352
|
+
// -------------------------------------------------------------------------
|
|
353
|
+
/** @deprecated Use user(request.headers) instead. */
|
|
354
|
+
async currentUser(request) {
|
|
355
|
+
return this.user(request.headers);
|
|
356
|
+
}
|
|
357
|
+
/** @deprecated Use session(request.headers) instead. */
|
|
358
|
+
async getSession(request) {
|
|
359
|
+
return this.session(request.headers);
|
|
360
|
+
}
|
|
361
|
+
// -------------------------------------------------------------------------
|
|
362
|
+
// Auth actions — call Better Auth endpoints on the stack's app
|
|
363
|
+
// -------------------------------------------------------------------------
|
|
364
|
+
/**
|
|
365
|
+
* Sign in with email and password.
|
|
366
|
+
*/
|
|
367
|
+
async signIn(params) {
|
|
368
|
+
const appUrl = resolveAppUrl(this._appUrl);
|
|
369
|
+
const data = await authApiFetch(appUrl, "/api/auth/sign-in/email", {
|
|
370
|
+
method: "POST",
|
|
371
|
+
body: params
|
|
372
|
+
});
|
|
373
|
+
return {
|
|
374
|
+
user: mapSessionUser(data.user),
|
|
375
|
+
sessionId: data.session.id,
|
|
376
|
+
expiresAt: new Date(data.session.expiresAt)
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Sign in with OAuth provider. Returns redirect URL.
|
|
381
|
+
*/
|
|
382
|
+
async signInWithProvider(provider, options) {
|
|
383
|
+
const appUrl = resolveAppUrl(this._appUrl);
|
|
384
|
+
const redirectTo = options?.redirectTo ?? "/";
|
|
385
|
+
if (!redirectTo.startsWith("/") || redirectTo.startsWith("//")) {
|
|
386
|
+
throw new BrokrError("[brokr] redirectTo must be a relative path (start with /)", "INVALID_REDIRECT", "auth");
|
|
387
|
+
}
|
|
388
|
+
if (!/^[a-z][a-z0-9_-]*$/.test(provider)) {
|
|
389
|
+
throw new BrokrError(`[brokr] Invalid provider name: "${provider}". Must be lowercase alphanumeric.`, "INVALID_PROVIDER", "auth");
|
|
390
|
+
}
|
|
391
|
+
const params = new URLSearchParams({ provider, callbackURL: redirectTo });
|
|
392
|
+
return { redirectUrl: `${appUrl}/api/auth/sign-in/social?${params}` };
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Sign up with email and password.
|
|
396
|
+
*/
|
|
397
|
+
async signUp(params) {
|
|
398
|
+
const appUrl = resolveAppUrl(this._appUrl);
|
|
399
|
+
const data = await authApiFetch(appUrl, "/api/auth/sign-up/email", {
|
|
400
|
+
method: "POST",
|
|
401
|
+
body: params
|
|
402
|
+
});
|
|
403
|
+
return {
|
|
404
|
+
user: mapSessionUser(data.user),
|
|
405
|
+
sessionId: data.session.id,
|
|
406
|
+
expiresAt: new Date(data.session.expiresAt)
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Sign out the current user.
|
|
411
|
+
*/
|
|
412
|
+
async signOut(headers) {
|
|
413
|
+
const appUrl = resolveAppUrl(this._appUrl);
|
|
414
|
+
const cookies = headers?.get("cookie") ?? "";
|
|
415
|
+
await authApiFetch(appUrl, "/api/auth/sign-out", {
|
|
416
|
+
method: "POST",
|
|
417
|
+
cookies
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Send a magic link email.
|
|
422
|
+
*/
|
|
423
|
+
async sendMagicLink(email, options) {
|
|
424
|
+
const appUrl = resolveAppUrl(this._appUrl);
|
|
425
|
+
await authApiFetch(appUrl, "/api/auth/magic-link/send", {
|
|
426
|
+
method: "POST",
|
|
427
|
+
body: { email, callbackURL: options?.redirectTo ?? "/" }
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Send a password reset email.
|
|
432
|
+
*/
|
|
433
|
+
async sendPasswordReset(params) {
|
|
434
|
+
const appUrl = resolveAppUrl(this._appUrl);
|
|
435
|
+
await authApiFetch(appUrl, "/api/auth/forget-password", {
|
|
436
|
+
method: "POST",
|
|
437
|
+
body: params
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Reset password with token from email.
|
|
442
|
+
*/
|
|
443
|
+
async resetPassword(params) {
|
|
444
|
+
const appUrl = resolveAppUrl(this._appUrl);
|
|
445
|
+
await authApiFetch(appUrl, "/api/auth/reset-password", {
|
|
446
|
+
method: "POST",
|
|
447
|
+
body: { token: params.token, newPassword: params.password }
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Verify email with token from email.
|
|
452
|
+
*/
|
|
453
|
+
async verifyEmail(params) {
|
|
454
|
+
const appUrl = resolveAppUrl(this._appUrl);
|
|
455
|
+
await authApiFetch(appUrl, "/api/auth/verify-email", {
|
|
456
|
+
method: "POST",
|
|
457
|
+
body: params
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Update the current user's profile.
|
|
462
|
+
*/
|
|
463
|
+
async updateUser(params, headers) {
|
|
464
|
+
const appUrl = resolveAppUrl(this._appUrl);
|
|
465
|
+
const cookies = headers?.get("cookie") ?? "";
|
|
466
|
+
await authApiFetch(appUrl, "/api/auth/update-user", {
|
|
467
|
+
method: "POST",
|
|
468
|
+
body: params,
|
|
469
|
+
cookies
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
// -------------------------------------------------------------------------
|
|
473
|
+
// Session management
|
|
474
|
+
// -------------------------------------------------------------------------
|
|
475
|
+
/** Session management sub-client. */
|
|
476
|
+
get sessions() {
|
|
477
|
+
if (!this._sessions) {
|
|
478
|
+
this._sessions = new BrokrSessionsClient(this._appUrl);
|
|
479
|
+
}
|
|
480
|
+
return this._sessions;
|
|
481
|
+
}
|
|
482
|
+
// -------------------------------------------------------------------------
|
|
483
|
+
// Ownership — queries the stack's own DB via gateway → server
|
|
484
|
+
// -------------------------------------------------------------------------
|
|
485
|
+
/**
|
|
486
|
+
* Check if the current user owns a resource. Throws 403 if not.
|
|
487
|
+
*
|
|
488
|
+
* Queries the developer's own database directly via DATABASE_URL.
|
|
489
|
+
* Convention: pluralizes resource name → table, assumes `id` PK + `user_id` owner column.
|
|
490
|
+
* Override with `opts.table` and `opts.ownerColumn` for non-standard schemas.
|
|
491
|
+
*
|
|
492
|
+
* @example
|
|
493
|
+
* ```ts
|
|
494
|
+
* await brokr.auth.requireOwner('conversation', conversationId, request.headers);
|
|
495
|
+
* // Under the hood: SELECT 1 FROM conversations WHERE id = $1 AND user_id = $2
|
|
496
|
+
* ```
|
|
497
|
+
*/
|
|
498
|
+
async requireOwner(resource, id, headers, opts) {
|
|
499
|
+
const isOwner = await this.isOwner(resource, id, headers, opts);
|
|
500
|
+
if (!isOwner) {
|
|
501
|
+
throw new BrokrError(
|
|
502
|
+
`[brokr] Access denied \u2014 current user does not own this ${resource}. Verify the resource ID and that the user is the owner.`,
|
|
503
|
+
"FORBIDDEN",
|
|
504
|
+
"auth"
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Check if the current user owns a resource. Returns boolean.
|
|
510
|
+
*
|
|
511
|
+
* Runs a direct query against the developer's database (DATABASE_URL).
|
|
512
|
+
* No gateway round-trip — this is a local DB operation.
|
|
513
|
+
*/
|
|
514
|
+
async isOwner(resource, id, headers, opts) {
|
|
515
|
+
const user = await this.requireUser(headers);
|
|
516
|
+
const dbUrl = typeof process !== "undefined" ? process.env.DATABASE_URL : void 0;
|
|
517
|
+
if (!dbUrl) {
|
|
518
|
+
throw new BrokrError(
|
|
519
|
+
"[brokr] DATABASE_URL is not set. Cannot check ownership without a database.",
|
|
520
|
+
"DB_NOT_CONFIGURED",
|
|
521
|
+
"auth"
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
const tableName = opts?.table ?? pluralizeResource(resource);
|
|
525
|
+
const ownerCol = opts?.ownerColumn ?? "user_id";
|
|
526
|
+
validateSqlIdentifier(tableName, "table name");
|
|
527
|
+
validateSqlIdentifier(ownerCol, "owner column");
|
|
528
|
+
const pool = await getOwnershipPool(dbUrl);
|
|
529
|
+
try {
|
|
530
|
+
const result = await pool.query(
|
|
531
|
+
`SELECT 1 FROM "${tableName}" WHERE id = $1 AND "${ownerCol}" = $2 LIMIT 1`,
|
|
532
|
+
[id, user.id]
|
|
533
|
+
);
|
|
534
|
+
return (result.rowCount ?? 0) > 0;
|
|
535
|
+
} catch (err) {
|
|
536
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
537
|
+
console.error("[brokr] Ownership check error:", msg);
|
|
538
|
+
if (msg.includes("does not exist")) {
|
|
539
|
+
throw new BrokrError(
|
|
540
|
+
`[brokr] Table "${tableName}" does not exist. Check your resource name or provide { table: '...' }.`,
|
|
541
|
+
"TABLE_NOT_FOUND",
|
|
542
|
+
"auth"
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
throw new BrokrError(
|
|
546
|
+
"[brokr] Ownership check failed. Check your DATABASE_URL and that the database is accessible.",
|
|
547
|
+
"DB_QUERY_FAILED",
|
|
548
|
+
"auth"
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Check if the current user matches a specific userId. Throws 403 if not.
|
|
554
|
+
*/
|
|
555
|
+
async requireCurrentUser(userId, headers) {
|
|
556
|
+
const user = await this.requireUser(headers);
|
|
557
|
+
if (user.id !== userId) {
|
|
558
|
+
throw new BrokrError("[brokr] Access denied.", "FORBIDDEN", "auth");
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
// -------------------------------------------------------------------------
|
|
562
|
+
// Legacy alias
|
|
563
|
+
// -------------------------------------------------------------------------
|
|
564
|
+
/** @deprecated Use signInWithProvider() instead. */
|
|
565
|
+
async getOAuthUrl(provider, options) {
|
|
566
|
+
return this.signInWithProvider(provider, options);
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
var BrokrSessionsClient = class {
|
|
570
|
+
constructor(appUrl) {
|
|
571
|
+
this._appUrl = appUrl;
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* List all active sessions for the current user.
|
|
575
|
+
*/
|
|
576
|
+
async list(headers) {
|
|
577
|
+
const appUrl = resolveAppUrl(this._appUrl);
|
|
578
|
+
const cookies = headers.get("cookie") ?? "";
|
|
579
|
+
const data = await authApiFetch(appUrl, "/api/auth/list-sessions", { cookies });
|
|
580
|
+
const sessionToken = parseCookieValue(cookies, "better-auth.session_token") ?? parseCookieValue(cookies, "__Secure-better-auth.session_token");
|
|
581
|
+
return data.map((s) => ({
|
|
582
|
+
id: s.id,
|
|
583
|
+
createdAt: new Date(s.createdAt),
|
|
584
|
+
expiresAt: new Date(s.expiresAt),
|
|
585
|
+
userAgent: s.userAgent,
|
|
586
|
+
ipAddress: s.ipAddress,
|
|
587
|
+
isCurrent: s.token === sessionToken
|
|
588
|
+
}));
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Revoke a specific session by ID.
|
|
592
|
+
*/
|
|
593
|
+
async revoke(sessionId, headers) {
|
|
594
|
+
const appUrl = resolveAppUrl(this._appUrl);
|
|
595
|
+
const cookies = headers.get("cookie") ?? "";
|
|
596
|
+
await authApiFetch(appUrl, "/api/auth/revoke-session", {
|
|
597
|
+
method: "POST",
|
|
598
|
+
body: { id: sessionId },
|
|
599
|
+
cookies
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Revoke all sessions except the current one.
|
|
604
|
+
*/
|
|
605
|
+
async revokeOthers(headers) {
|
|
606
|
+
const appUrl = resolveAppUrl(this._appUrl);
|
|
607
|
+
const cookies = headers.get("cookie") ?? "";
|
|
608
|
+
await authApiFetch(appUrl, "/api/auth/revoke-other-sessions", {
|
|
609
|
+
method: "POST",
|
|
610
|
+
cookies
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
};
|
|
24
614
|
function parseCookies(cookieHeader) {
|
|
25
615
|
const cookies = /* @__PURE__ */ new Map();
|
|
26
616
|
for (const pair of cookieHeader.split(";")) {
|
|
@@ -32,6 +622,9 @@ function parseCookies(cookieHeader) {
|
|
|
32
622
|
}
|
|
33
623
|
return cookies;
|
|
34
624
|
}
|
|
625
|
+
function parseCookieValue(cookieHeader, name) {
|
|
626
|
+
return parseCookies(cookieHeader).get(name);
|
|
627
|
+
}
|
|
35
628
|
function authMiddleware(options) {
|
|
36
629
|
const { protectedRoutes = [], publicOnlyRoutes = [] } = options;
|
|
37
630
|
return async function middleware(request) {
|
|
@@ -42,7 +635,7 @@ function authMiddleware(options) {
|
|
|
42
635
|
if (!isProtected && !isPublicOnly) return void 0;
|
|
43
636
|
const cookieHeader = request.headers.get("cookie") ?? "";
|
|
44
637
|
const cookies = parseCookies(cookieHeader);
|
|
45
|
-
const hasSession = cookies.has("better-auth.session_token");
|
|
638
|
+
const hasSession = cookies.has("better-auth.session_token") || cookies.has("__Secure-better-auth.session_token");
|
|
46
639
|
if (isProtected && !hasSession) {
|
|
47
640
|
return Response.redirect(new URL("/sign-in", request.url).toString(), 302);
|
|
48
641
|
}
|
|
@@ -52,99 +645,6 @@ function authMiddleware(options) {
|
|
|
52
645
|
return void 0;
|
|
53
646
|
};
|
|
54
647
|
}
|
|
55
|
-
var BrokrAuthClient;
|
|
56
|
-
var init_auth = __esm({
|
|
57
|
-
"src/auth.ts"() {
|
|
58
|
-
init_runtime();
|
|
59
|
-
BrokrAuthClient = class {
|
|
60
|
-
constructor(token, gatewayUrl, appUrl) {
|
|
61
|
-
this._token = token;
|
|
62
|
-
this._gatewayUrl = gatewayUrl;
|
|
63
|
-
this._appUrl = appUrl ?? (typeof process !== "undefined" ? process.env.BETTER_AUTH_URL : void 0);
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Get current user from an incoming request's cookies.
|
|
67
|
-
* Calls the local Better Auth API (runs inside your app).
|
|
68
|
-
*/
|
|
69
|
-
async currentUser(request) {
|
|
70
|
-
const session = await this.getSession(request);
|
|
71
|
-
return session?.user ?? null;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Get the full session (user + metadata) from an incoming request.
|
|
75
|
-
* Calls the local Better Auth API.
|
|
76
|
-
*/
|
|
77
|
-
async getSession(request) {
|
|
78
|
-
const appUrl = this._appUrl;
|
|
79
|
-
if (!appUrl) {
|
|
80
|
-
throw new BrokrError(
|
|
81
|
-
"[brokr] BETTER_AUTH_URL is not set. Auth may not be provisioned.",
|
|
82
|
-
"AUTH_NOT_CONFIGURED",
|
|
83
|
-
"auth"
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
const cookieHeader = request.headers.get("cookie") ?? "";
|
|
87
|
-
if (!cookieHeader) return null;
|
|
88
|
-
const res = await fetch(`${appUrl}/api/auth/get-session`, {
|
|
89
|
-
method: "GET",
|
|
90
|
-
headers: { cookie: cookieHeader }
|
|
91
|
-
});
|
|
92
|
-
if (!res.ok) return null;
|
|
93
|
-
const data = await res.json();
|
|
94
|
-
if (!data?.user || !data?.session) return null;
|
|
95
|
-
return {
|
|
96
|
-
user: {
|
|
97
|
-
id: data.user.id,
|
|
98
|
-
email: data.user.email,
|
|
99
|
-
name: data.user.name ?? null,
|
|
100
|
-
avatarUrl: data.user.image ?? null,
|
|
101
|
-
emailVerified: data.user.emailVerified ? /* @__PURE__ */ new Date() : null
|
|
102
|
-
},
|
|
103
|
-
sessionId: data.session.id,
|
|
104
|
-
expiresAt: new Date(data.session.expiresAt)
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Generate an OAuth authorization URL.
|
|
109
|
-
*/
|
|
110
|
-
async getOAuthUrl(provider, options) {
|
|
111
|
-
const appUrl = this._appUrl;
|
|
112
|
-
if (!appUrl) {
|
|
113
|
-
throw new BrokrError("[brokr] BETTER_AUTH_URL is not set.", "AUTH_NOT_CONFIGURED", "auth");
|
|
114
|
-
}
|
|
115
|
-
const redirectTo = options?.redirectTo ?? "/";
|
|
116
|
-
if (!redirectTo.startsWith("/") || redirectTo.startsWith("//")) {
|
|
117
|
-
throw new BrokrError("[brokr] redirectTo must be a relative path (start with /)", "INVALID_REDIRECT", "auth");
|
|
118
|
-
}
|
|
119
|
-
const params = new URLSearchParams({ callbackURL: redirectTo });
|
|
120
|
-
return { redirectUrl: `${appUrl}/api/auth/sign-in/social?provider=${provider}&${params}` };
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Send a magic link email (requires email capability).
|
|
124
|
-
*/
|
|
125
|
-
async sendMagicLink(email, options) {
|
|
126
|
-
const appUrl = this._appUrl;
|
|
127
|
-
if (!appUrl) {
|
|
128
|
-
throw new BrokrError("[brokr] BETTER_AUTH_URL is not set.", "AUTH_NOT_CONFIGURED", "auth");
|
|
129
|
-
}
|
|
130
|
-
const res = await fetch(`${appUrl}/api/auth/magic-link/send`, {
|
|
131
|
-
method: "POST",
|
|
132
|
-
headers: { "Content-Type": "application/json" },
|
|
133
|
-
body: JSON.stringify({ email, callbackURL: options?.redirectTo ?? "/" })
|
|
134
|
-
});
|
|
135
|
-
if (!res.ok) {
|
|
136
|
-
const data = await res.json().catch(() => ({}));
|
|
137
|
-
throw new BrokrError(
|
|
138
|
-
data.message ?? `[brokr] Failed to send magic link (HTTP ${res.status})`,
|
|
139
|
-
"AUTH_MAGIC_LINK_FAILED",
|
|
140
|
-
"auth"
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
init_auth();
|
|
148
648
|
export {
|
|
149
649
|
BrokrAuthClient,
|
|
150
650
|
authMiddleware
|