@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.
Files changed (298) hide show
  1. package/dist/account.js +34 -0
  2. package/dist/account.mjs +7 -0
  3. package/dist/auth.js +628 -114
  4. package/dist/auth.mjs +611 -111
  5. package/dist/chat.js +34 -0
  6. package/dist/chat.mjs +7 -0
  7. package/dist/events.js +64 -0
  8. package/dist/events.mjs +37 -0
  9. package/dist/feature.js +6304 -0
  10. package/dist/feature.mjs +6278 -0
  11. package/dist/files.js +428 -0
  12. package/dist/files.mjs +408 -0
  13. package/dist/index.d.ts +18 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +4069 -454
  16. package/dist/index.mjs +4040 -448
  17. package/dist/logs.js +148 -0
  18. package/dist/logs.mjs +124 -0
  19. package/dist/management.js +14 -13
  20. package/dist/management.mjs +14 -13
  21. package/dist/next.js +2725 -0
  22. package/dist/next.mjs +2710 -0
  23. package/dist/notifications.js +140 -0
  24. package/dist/notifications.mjs +110 -0
  25. package/dist/payments.js +32 -0
  26. package/dist/payments.mjs +7 -0
  27. package/dist/react-notifications.js +286 -0
  28. package/dist/react-notifications.mjs +254 -0
  29. package/dist/react-styles.js +2718 -0
  30. package/dist/react-styles.mjs +2682 -0
  31. package/dist/react-theme.js +4194 -0
  32. package/dist/react-theme.mjs +4170 -0
  33. package/dist/react.js +8512 -209
  34. package/dist/react.mjs +8488 -179
  35. package/dist/runtime.js +2113 -385
  36. package/dist/runtime.mjs +2085 -397
  37. package/dist/src/account/config.d.ts +42 -0
  38. package/dist/src/account/config.d.ts.map +1 -0
  39. package/dist/src/account/index.d.ts +3 -0
  40. package/dist/src/account/index.d.ts.map +1 -0
  41. package/dist/src/ai/client.d.ts +58 -0
  42. package/dist/src/ai/client.d.ts.map +1 -0
  43. package/dist/src/ai/conversation-title.d.ts +13 -0
  44. package/dist/src/ai/conversation-title.d.ts.map +1 -0
  45. package/dist/src/ai/types.d.ts +81 -0
  46. package/dist/src/ai/types.d.ts.map +1 -0
  47. package/dist/src/auth.d.ts +133 -20
  48. package/dist/src/auth.d.ts.map +1 -1
  49. package/dist/src/chat/config.d.ts +61 -0
  50. package/dist/src/chat/config.d.ts.map +1 -0
  51. package/dist/src/chat/index.d.ts +3 -0
  52. package/dist/src/chat/index.d.ts.map +1 -0
  53. package/dist/src/chat/sse-parser.d.ts +44 -0
  54. package/dist/src/chat/sse-parser.d.ts.map +1 -0
  55. package/dist/src/dev-console.d.ts +18 -0
  56. package/dist/src/dev-console.d.ts.map +1 -0
  57. package/dist/src/email/client.d.ts +33 -0
  58. package/dist/src/email/client.d.ts.map +1 -0
  59. package/dist/src/email/templates.d.ts +15 -0
  60. package/dist/src/email/templates.d.ts.map +1 -0
  61. package/dist/src/email/types.d.ts +35 -0
  62. package/dist/src/email/types.d.ts.map +1 -0
  63. package/dist/src/env-detect.d.ts +25 -0
  64. package/dist/src/env-detect.d.ts.map +1 -0
  65. package/dist/src/errors.d.ts +53 -0
  66. package/dist/src/errors.d.ts.map +1 -0
  67. package/dist/src/events/client.d.ts +25 -0
  68. package/dist/src/events/client.d.ts.map +1 -0
  69. package/dist/src/events/index.d.ts +9 -0
  70. package/dist/src/events/index.d.ts.map +1 -0
  71. package/dist/src/events/types.d.ts +10 -0
  72. package/dist/src/events/types.d.ts.map +1 -0
  73. package/dist/src/feature/canonical.d.ts +33 -0
  74. package/dist/src/feature/canonical.d.ts.map +1 -0
  75. package/dist/src/feature/create-feature.d.ts +33 -0
  76. package/dist/src/feature/create-feature.d.ts.map +1 -0
  77. package/dist/src/feature/db.d.ts +16 -0
  78. package/dist/src/feature/db.d.ts.map +1 -0
  79. package/dist/src/feature/handlers.d.ts +28 -0
  80. package/dist/src/feature/handlers.d.ts.map +1 -0
  81. package/dist/src/feature/index.d.ts +21 -0
  82. package/dist/src/feature/index.d.ts.map +1 -0
  83. package/dist/src/feature/manifest.d.ts +173 -0
  84. package/dist/src/feature/manifest.d.ts.map +1 -0
  85. package/dist/src/feature/mapping.d.ts +18 -0
  86. package/dist/src/feature/mapping.d.ts.map +1 -0
  87. package/dist/src/feature/runtime.d.ts +45 -0
  88. package/dist/src/feature/runtime.d.ts.map +1 -0
  89. package/dist/src/feature/types.d.ts +65 -0
  90. package/dist/src/feature/types.d.ts.map +1 -0
  91. package/dist/src/files/client.d.ts +28 -0
  92. package/dist/src/files/client.d.ts.map +1 -0
  93. package/dist/src/files/types.d.ts +28 -0
  94. package/dist/src/files/types.d.ts.map +1 -0
  95. package/dist/src/fix-registry.d.ts +8 -0
  96. package/dist/src/fix-registry.d.ts.map +1 -0
  97. package/dist/src/gateway.d.ts +32 -0
  98. package/dist/src/gateway.d.ts.map +1 -0
  99. package/dist/src/logs/capture.d.ts +56 -0
  100. package/dist/src/logs/capture.d.ts.map +1 -0
  101. package/dist/src/logs/index.d.ts +2 -0
  102. package/dist/src/logs/index.d.ts.map +1 -0
  103. package/dist/src/management.d.ts +1 -1
  104. package/dist/src/management.d.ts.map +1 -1
  105. package/dist/src/models.d.ts +32 -0
  106. package/dist/src/models.d.ts.map +1 -0
  107. package/dist/src/next/auth.d.ts +54 -0
  108. package/dist/src/next/auth.d.ts.map +1 -0
  109. package/dist/src/next/chat.d.ts +31 -0
  110. package/dist/src/next/chat.d.ts.map +1 -0
  111. package/dist/src/next/index.d.ts +14 -0
  112. package/dist/src/next/index.d.ts.map +1 -0
  113. package/dist/src/next/notifications.d.ts +67 -0
  114. package/dist/src/next/notifications.d.ts.map +1 -0
  115. package/dist/src/notifications/built-ins.d.ts +9 -0
  116. package/dist/src/notifications/built-ins.d.ts.map +1 -0
  117. package/dist/src/notifications/client.d.ts +38 -0
  118. package/dist/src/notifications/client.d.ts.map +1 -0
  119. package/dist/src/notifications/config.d.ts +71 -0
  120. package/dist/src/notifications/config.d.ts.map +1 -0
  121. package/dist/src/notifications/index.d.ts +6 -0
  122. package/dist/src/notifications/index.d.ts.map +1 -0
  123. package/dist/src/notifications/registry.d.ts +67 -0
  124. package/dist/src/notifications/registry.d.ts.map +1 -0
  125. package/dist/src/notifications/types.d.ts +48 -0
  126. package/dist/src/notifications/types.d.ts.map +1 -0
  127. package/dist/src/payments/client.d.ts +64 -0
  128. package/dist/src/payments/client.d.ts.map +1 -0
  129. package/dist/src/payments/config.d.ts +46 -0
  130. package/dist/src/payments/config.d.ts.map +1 -0
  131. package/dist/src/payments/entitlements.d.ts +48 -0
  132. package/dist/src/payments/entitlements.d.ts.map +1 -0
  133. package/dist/src/payments/types.d.ts +135 -0
  134. package/dist/src/payments/types.d.ts.map +1 -0
  135. package/dist/src/react/BrokrErrorBoundary.d.ts +23 -0
  136. package/dist/src/react/BrokrErrorBoundary.d.ts.map +1 -0
  137. package/dist/src/react/account/AccountPanel.d.ts +12 -0
  138. package/dist/src/react/account/AccountPanel.d.ts.map +1 -0
  139. package/dist/src/react/account/Avatar.d.ts +11 -0
  140. package/dist/src/react/account/Avatar.d.ts.map +1 -0
  141. package/dist/src/react/account/ProfilePhotoButton.d.ts +7 -0
  142. package/dist/src/react/account/ProfilePhotoButton.d.ts.map +1 -0
  143. package/dist/src/react/account/UserButton.d.ts +7 -0
  144. package/dist/src/react/account/UserButton.d.ts.map +1 -0
  145. package/dist/src/react/auth-pages/AuthPageShell.d.ts +9 -0
  146. package/dist/src/react/auth-pages/AuthPageShell.d.ts.map +1 -0
  147. package/dist/src/react/auth-pages/SignInPage.d.ts +9 -0
  148. package/dist/src/react/auth-pages/SignInPage.d.ts.map +1 -0
  149. package/dist/src/react/auth-pages/SignUpPage.d.ts +8 -0
  150. package/dist/src/react/auth-pages/SignUpPage.d.ts.map +1 -0
  151. package/dist/src/react/auth.d.ts +1 -49
  152. package/dist/src/react/auth.d.ts.map +1 -1
  153. package/dist/src/react/chat/AIChat.d.ts +4 -0
  154. package/dist/src/react/chat/AIChat.d.ts.map +1 -0
  155. package/dist/src/react/chat/ChatContext.d.ts +76 -0
  156. package/dist/src/react/chat/ChatContext.d.ts.map +1 -0
  157. package/dist/src/react/chat/ChatInput.d.ts +3 -0
  158. package/dist/src/react/chat/ChatInput.d.ts.map +1 -0
  159. package/dist/src/react/chat/MarkdownRenderer.d.ts +5 -0
  160. package/dist/src/react/chat/MarkdownRenderer.d.ts.map +1 -0
  161. package/dist/src/react/chat/MessageBubble.d.ts +14 -0
  162. package/dist/src/react/chat/MessageBubble.d.ts.map +1 -0
  163. package/dist/src/react/chat/MessagePane.d.ts +10 -0
  164. package/dist/src/react/chat/MessagePane.d.ts.map +1 -0
  165. package/dist/src/react/chat/ModelSelector.d.ts +13 -0
  166. package/dist/src/react/chat/ModelSelector.d.ts.map +1 -0
  167. package/dist/src/react/chat/ThreadSidebar.d.ts +3 -0
  168. package/dist/src/react/chat/ThreadSidebar.d.ts.map +1 -0
  169. package/dist/src/react/chat/index.d.ts +5 -0
  170. package/dist/src/react/chat/index.d.ts.map +1 -0
  171. package/dist/src/react/chat/token-limit.d.ts +14 -0
  172. package/dist/src/react/chat/token-limit.d.ts.map +1 -0
  173. package/dist/src/react/chat/types.d.ts +65 -0
  174. package/dist/src/react/chat/types.d.ts.map +1 -0
  175. package/dist/src/react/chat/useChat.d.ts +57 -0
  176. package/dist/src/react/chat/useChat.d.ts.map +1 -0
  177. package/dist/src/react/composites/FabAI.d.ts +15 -0
  178. package/dist/src/react/composites/FabAI.d.ts.map +1 -0
  179. package/dist/src/react/composites/FeedbackWidget.d.ts +10 -0
  180. package/dist/src/react/composites/FeedbackWidget.d.ts.map +1 -0
  181. package/dist/src/react/composites/SmartUpload.d.ts +12 -0
  182. package/dist/src/react/composites/SmartUpload.d.ts.map +1 -0
  183. package/dist/src/react/config.d.ts +23 -0
  184. package/dist/src/react/config.d.ts.map +1 -0
  185. package/dist/src/react/context.d.ts +4 -0
  186. package/dist/src/react/context.d.ts.map +1 -0
  187. package/dist/src/react/css/account.d.ts +2 -0
  188. package/dist/src/react/css/account.d.ts.map +1 -0
  189. package/dist/src/react/css/animations.d.ts +2 -0
  190. package/dist/src/react/css/animations.d.ts.map +1 -0
  191. package/dist/src/react/css/auth.d.ts +2 -0
  192. package/dist/src/react/css/auth.d.ts.map +1 -0
  193. package/dist/src/react/css/chat-extras.d.ts +2 -0
  194. package/dist/src/react/css/chat-extras.d.ts.map +1 -0
  195. package/dist/src/react/css/chat.d.ts +2 -0
  196. package/dist/src/react/css/chat.d.ts.map +1 -0
  197. package/dist/src/react/css/composites.d.ts +2 -0
  198. package/dist/src/react/css/composites.d.ts.map +1 -0
  199. package/dist/src/react/css/gates.d.ts +2 -0
  200. package/dist/src/react/css/gates.d.ts.map +1 -0
  201. package/dist/src/react/css/index.d.ts +3 -0
  202. package/dist/src/react/css/index.d.ts.map +1 -0
  203. package/dist/src/react/css/markdown.d.ts +2 -0
  204. package/dist/src/react/css/markdown.d.ts.map +1 -0
  205. package/dist/src/react/css/notifications.d.ts +2 -0
  206. package/dist/src/react/css/notifications.d.ts.map +1 -0
  207. package/dist/src/react/css/primitives.d.ts +2 -0
  208. package/dist/src/react/css/primitives.d.ts.map +1 -0
  209. package/dist/src/react/css/reset.d.ts +2 -0
  210. package/dist/src/react/css/reset.d.ts.map +1 -0
  211. package/dist/src/react/css/responsive.d.ts +2 -0
  212. package/dist/src/react/css/responsive.d.ts.map +1 -0
  213. package/dist/src/react/css/skeleton.d.ts +2 -0
  214. package/dist/src/react/css/skeleton.d.ts.map +1 -0
  215. package/dist/src/react/css/tokens.d.ts +2 -0
  216. package/dist/src/react/css/tokens.d.ts.map +1 -0
  217. package/dist/src/react/gates/AuthWall.d.ts +7 -0
  218. package/dist/src/react/gates/AuthWall.d.ts.map +1 -0
  219. package/dist/src/react/gates/BillingBoundary.d.ts +4 -0
  220. package/dist/src/react/gates/BillingBoundary.d.ts.map +1 -0
  221. package/dist/src/react/gates/Gate.d.ts +9 -0
  222. package/dist/src/react/gates/Gate.d.ts.map +1 -0
  223. package/dist/src/react/gates/RequirePlan.d.ts +4 -0
  224. package/dist/src/react/gates/RequirePlan.d.ts.map +1 -0
  225. package/dist/src/react/gates/RequireUser.d.ts +4 -0
  226. package/dist/src/react/gates/RequireUser.d.ts.map +1 -0
  227. package/dist/src/react/gates/UsageGate.d.ts +4 -0
  228. package/dist/src/react/gates/UsageGate.d.ts.map +1 -0
  229. package/dist/src/react/helpers.d.ts +7 -0
  230. package/dist/src/react/helpers.d.ts.map +1 -0
  231. package/dist/src/react/hooks/use-theme.d.ts +15 -0
  232. package/dist/src/react/hooks/use-theme.d.ts.map +1 -0
  233. package/dist/src/react/hooks/use-user.d.ts +12 -0
  234. package/dist/src/react/hooks/use-user.d.ts.map +1 -0
  235. package/dist/src/react/icons.d.ts +26 -0
  236. package/dist/src/react/icons.d.ts.map +1 -0
  237. package/dist/src/react/index.d.ts +48 -0
  238. package/dist/src/react/index.d.ts.map +1 -0
  239. package/dist/src/react/notifications/NotificationBell.d.ts +7 -0
  240. package/dist/src/react/notifications/NotificationBell.d.ts.map +1 -0
  241. package/dist/src/react/notifications/NotificationList.d.ts +7 -0
  242. package/dist/src/react/notifications/NotificationList.d.ts.map +1 -0
  243. package/dist/src/react/notifications/Toast.d.ts +13 -0
  244. package/dist/src/react/notifications/Toast.d.ts.map +1 -0
  245. package/dist/src/react/notifications/index.d.ts +8 -0
  246. package/dist/src/react/notifications/index.d.ts.map +1 -0
  247. package/dist/src/react/notifications/provider.d.ts +14 -0
  248. package/dist/src/react/notifications/provider.d.ts.map +1 -0
  249. package/dist/src/react/notifications/use-notifications.d.ts +24 -0
  250. package/dist/src/react/notifications/use-notifications.d.ts.map +1 -0
  251. package/dist/src/react/payments/AutoReloadToggle.d.ts +6 -0
  252. package/dist/src/react/payments/AutoReloadToggle.d.ts.map +1 -0
  253. package/dist/src/react/payments/Balance.d.ts +8 -0
  254. package/dist/src/react/payments/Balance.d.ts.map +1 -0
  255. package/dist/src/react/payments/CancelSubscription.d.ts +6 -0
  256. package/dist/src/react/payments/CancelSubscription.d.ts.map +1 -0
  257. package/dist/src/react/payments/CheckoutButton.d.ts +7 -0
  258. package/dist/src/react/payments/CheckoutButton.d.ts.map +1 -0
  259. package/dist/src/react/payments/CustomerPortalButton.d.ts +6 -0
  260. package/dist/src/react/payments/CustomerPortalButton.d.ts.map +1 -0
  261. package/dist/src/react/payments/FeatureMeter.d.ts +6 -0
  262. package/dist/src/react/payments/FeatureMeter.d.ts.map +1 -0
  263. package/dist/src/react/payments/Plans.d.ts +8 -0
  264. package/dist/src/react/payments/Plans.d.ts.map +1 -0
  265. package/dist/src/react/payments/TopUpButton.d.ts +8 -0
  266. package/dist/src/react/payments/TopUpButton.d.ts.map +1 -0
  267. package/dist/src/react/payments/UpdateBilling.d.ts +3 -0
  268. package/dist/src/react/payments/UpdateBilling.d.ts.map +1 -0
  269. package/dist/src/react/payments/UpgradePrompt.d.ts +8 -0
  270. package/dist/src/react/payments/UpgradePrompt.d.ts.map +1 -0
  271. package/dist/src/react/primitives/Skeleton.d.ts +15 -0
  272. package/dist/src/react/primitives/Skeleton.d.ts.map +1 -0
  273. package/dist/src/react/provider.d.ts +21 -0
  274. package/dist/src/react/provider.d.ts.map +1 -0
  275. package/dist/src/react/request.d.ts +2 -0
  276. package/dist/src/react/request.d.ts.map +1 -0
  277. package/dist/src/react/styles-entry.d.ts +4 -0
  278. package/dist/src/react/styles-entry.d.ts.map +1 -0
  279. package/dist/src/react/styles.d.ts +2 -0
  280. package/dist/src/react/styles.d.ts.map +1 -0
  281. package/dist/src/react/theme-entry.d.ts +3 -0
  282. package/dist/src/react/theme-entry.d.ts.map +1 -0
  283. package/dist/src/react/theme.d.ts +6 -0
  284. package/dist/src/react/theme.d.ts.map +1 -0
  285. package/dist/src/react/types.d.ts +191 -0
  286. package/dist/src/react/types.d.ts.map +1 -0
  287. package/dist/src/react/use-brokr-theme.d.ts +6 -0
  288. package/dist/src/react/use-brokr-theme.d.ts.map +1 -0
  289. package/dist/src/runtime.d.ts +69 -180
  290. package/dist/src/runtime.d.ts.map +1 -1
  291. package/dist/src/storage/client.d.ts +113 -0
  292. package/dist/src/storage/client.d.ts.map +1 -0
  293. package/dist/src/storage/types.d.ts +60 -0
  294. package/dist/src/storage/types.d.ts.map +1 -0
  295. package/dist/tsconfig.tsbuildinfo +1 -1
  296. package/dist/types.d.ts +2 -2
  297. package/dist/types.d.ts.map +1 -1
  298. package/package.json +70 -9
package/dist/runtime.mjs CHANGED
@@ -1,166 +1,275 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __hasOwnProp = Object.prototype.hasOwnProperty;
5
1
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
6
2
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
7
3
  }) : x)(function(x) {
8
4
  if (typeof require !== "undefined") return require.apply(this, arguments);
9
5
  throw Error('Dynamic require of "' + x + '" is not supported');
10
6
  });
11
- var __esm = (fn, res) => function __init() {
12
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
7
+
8
+ // src/fix-registry.ts
9
+ var FIX_REGISTRY = {
10
+ AUTH_TOKEN_INVALID: [
11
+ "\u2192 Run `brokr env pull --stack <name>` to refresh your token",
12
+ "\u2192 Or run `brokr link account` to re-authenticate"
13
+ ].join("\n"),
14
+ AUTH_SESSION_EXPIRED: [
15
+ "\u2192 Run `brokr link account` to re-authenticate"
16
+ ].join("\n"),
17
+ BROKR_TOKEN_MISSING: [
18
+ "\u2192 Run `brokr env pull --stack <name>` to sync your environment",
19
+ "\u2192 Or add BROKR_TOKEN to your .env.local manually"
20
+ ].join("\n"),
21
+ DATABASE_PROVISION_FAILED: [
22
+ "\u2192 Run `brokr status <stack>` to check current state",
23
+ "\u2192 Run `brokr retry <stack>` to re-trigger provisioning"
24
+ ].join("\n"),
25
+ DATABASE_QUOTA_REACHED: [
26
+ "\u2192 Upgrade your plan or delete unused databases",
27
+ "\u2192 Check usage at brokr.sh/billing"
28
+ ].join("\n"),
29
+ DATABASE_HEALTH_TIMEOUT: [
30
+ "\u2192 Run `brokr status <stack>` to check database state",
31
+ "\u2192 If persistent, check provider status page"
32
+ ].join("\n"),
33
+ DEPLOYMENT_FAILED: [
34
+ "\u2192 Run `brokr logs --stack <name>` to see build logs",
35
+ "\u2192 Fix the build error and run `brokr deploy`"
36
+ ].join("\n"),
37
+ DEPLOYMENT_RATE_LIMITED: [
38
+ "\u2192 Wait a minute and retry",
39
+ "\u2192 Vercel rate limits apply per project"
40
+ ].join("\n"),
41
+ REPO_ALREADY_EXISTS: [
42
+ "\u2192 Use a different stack name",
43
+ "\u2192 Or run `brokr status <stack>` to check the existing stack"
44
+ ].join("\n"),
45
+ REPO_CREATE_FAILED: [
46
+ "\u2192 Check your GitHub connection: `brokr link account`",
47
+ "\u2192 Ensure the Brokr GitHub App is installed"
48
+ ].join("\n"),
49
+ EMAIL_DOMAIN_FAILED: [
50
+ "\u2192 Check DNS records are propagated",
51
+ "\u2192 Run `brokr status <stack>` for details"
52
+ ].join("\n"),
53
+ DNS_RECORD_FAILED: [
54
+ "\u2192 Check Cloudflare dashboard for zone status",
55
+ "\u2192 Retry with `brokr retry <stack>`"
56
+ ].join("\n"),
57
+ INSUFFICIENT_CREDITS: [
58
+ "\u2192 Top up credits at brokr.sh/billing",
59
+ "\u2192 Or run `brokr billing topup`"
60
+ ].join("\n"),
61
+ AI_BUDGET_EXCEEDED: [
62
+ "\u2192 Top up credits at brokr.sh/billing",
63
+ "\u2192 Or run `brokr billing topup`"
64
+ ].join("\n"),
65
+ AI_PROVIDER_UNAVAILABLE: [
66
+ "\u2192 The AI provider is temporarily down",
67
+ "\u2192 Retry in a few seconds \u2014 this is usually transient"
68
+ ].join("\n"),
69
+ AI_MODEL_NOT_FOUND: [
70
+ "\u2192 Check the model name in your configuration",
71
+ "\u2192 See available models at brokr.sh/docs/ai-models"
72
+ ].join("\n"),
73
+ ENV_VAR_MISSING: [
74
+ "\u2192 Run `brokr env pull --stack <name>` to sync environment",
75
+ "\u2192 Check required vars in your template README"
76
+ ].join("\n"),
77
+ STACK_LIMIT_REACHED: [
78
+ "\u2192 Delete unused stacks with `brokr delete <stack>`",
79
+ "\u2192 Or upgrade your plan at brokr.sh/billing"
80
+ ].join("\n"),
81
+ STACK_NOT_FOUND: [
82
+ "\u2192 Check the stack name: `brokr list`",
83
+ "\u2192 Create a new stack: `brokr create --name <name>`"
84
+ ].join("\n"),
85
+ RATE_LIMITED: [
86
+ "\u2192 Wait a moment and retry",
87
+ "\u2192 If persistent, check brokr.sh/status"
88
+ ].join("\n"),
89
+ GATEWAY_AUTH_FAILED: [
90
+ "\u2192 Your BROKR_TOKEN may be expired",
91
+ "\u2192 Run `brokr env pull --stack <name>` to refresh"
92
+ ].join("\n"),
93
+ BROKR_TOKEN_INVALID: [
94
+ "\u2192 Your BROKR_TOKEN has expired or been revoked",
95
+ "\u2192 Run `brokr env pull --stack <name>` to get a fresh token"
96
+ ].join("\n"),
97
+ AI_STREAM_ERROR: [
98
+ "\u2192 The AI stream was interrupted",
99
+ "\u2192 Retry the request \u2014 this is usually transient"
100
+ ].join("\n"),
101
+ EMAIL_SEND_FAILED: [
102
+ "\u2192 Email delivery failed",
103
+ "\u2192 Check that your domain DNS is verified: `brokr status <stack>`"
104
+ ].join("\n"),
105
+ EMAIL_NOT_CONFIGURED: [
106
+ "\u2192 Email is not set up for this stack",
107
+ "\u2192 Add email capability: `brokr add email --stack <name>`"
108
+ ].join("\n"),
109
+ EMAIL_INVALID_FROM: [
110
+ "\u2192 The from address must match your verified domain",
111
+ "\u2192 Check your domain: `brokr status <stack>`"
112
+ ].join("\n"),
113
+ STORAGE_UPLOAD_FAILED: [
114
+ "\u2192 File upload failed",
115
+ "\u2192 Check file size limits and retry"
116
+ ].join("\n"),
117
+ STORAGE_NOT_FOUND: [
118
+ "\u2192 The requested file does not exist",
119
+ "\u2192 Check the key and try again"
120
+ ].join("\n"),
121
+ PAYMENTS_NOT_CONFIGURED: [
122
+ "\u2192 Payments are not set up for this stack",
123
+ "\u2192 Run `brokr payments sync` to configure Stripe"
124
+ ].join("\n"),
125
+ PAYMENTS_FAILED: [
126
+ "\u2192 Payment processing failed",
127
+ "\u2192 Check your Stripe dashboard for details"
128
+ ].join("\n"),
129
+ GATEWAY_INTERNAL: [
130
+ "\u2192 The Brokr gateway encountered an internal error",
131
+ "\u2192 Check brokr.sh/status for service health"
132
+ ].join("\n"),
133
+ UPSTREAM_ERROR: [
134
+ "\u2192 An upstream service is temporarily unavailable",
135
+ "\u2192 Retry in a few seconds \u2014 this is usually transient"
136
+ ].join("\n"),
137
+ NETWORK_ERROR: [
138
+ "\u2192 Could not reach the Brokr gateway",
139
+ "\u2192 Check your internet connection",
140
+ "\u2192 Check brokr.sh/status for service health"
141
+ ].join("\n"),
142
+ TIMEOUT: [
143
+ "\u2192 Request timed out",
144
+ "\u2192 Retry \u2014 if persistent, check brokr.sh/status"
145
+ ].join("\n")
13
146
  };
14
- var __export = (target, all) => {
15
- for (var name in all)
16
- __defProp(target, name, { get: all[name], enumerable: true });
147
+
148
+ // src/errors.ts
149
+ var BrokrError = class extends Error {
150
+ constructor(message, code, capability, retryable = false, errorCode, requestId, hint) {
151
+ super(message);
152
+ this.code = code;
153
+ this.capability = capability;
154
+ this.retryable = retryable;
155
+ this.errorCode = errorCode;
156
+ this.requestId = requestId;
157
+ this.hint = hint;
158
+ this.name = "BrokrError";
159
+ }
160
+ /** Multi-line terminal fix block — looked up from error code. */
161
+ get fix() {
162
+ if (!this.errorCode) return void 0;
163
+ return FIX_REGISTRY[this.errorCode];
164
+ }
165
+ /** Documentation URL for this error code. */
166
+ get docsUrl() {
167
+ if (!this.errorCode) return void 0;
168
+ return `https://brokr.sh/errors/${this.errorCode.toLowerCase().replace(/_/g, "-")}`;
169
+ }
170
+ /** Safe message for end users — zero technical language. */
171
+ toUserMessage() {
172
+ switch (this.code) {
173
+ case "RATE_LIMITED":
174
+ return "Please wait a moment and try again.";
175
+ case "TIMEOUT":
176
+ return "The request took too long. Please try again.";
177
+ case "NETWORK_ERROR":
178
+ return "Connection issue. Check your internet and try again.";
179
+ case "BROKR_TOKEN_MISSING":
180
+ return "App is not connected to Brokr. Contact the developer.";
181
+ case "BROKR_TOKEN_INVALID":
182
+ return "Session expired. Please refresh.";
183
+ case "AI_BUDGET_EXCEEDED":
184
+ return "AI usage limit reached. The developer needs to add credits.";
185
+ case "AI_PROVIDER_UNAVAILABLE":
186
+ return "AI service is temporarily down. Please retry shortly.";
187
+ case "AI_PROVIDER_RATE_LIMITED":
188
+ return "Too many AI requests. Please wait a moment.";
189
+ case "INSUFFICIENT_CREDITS":
190
+ return "Not enough credits. Please top up your balance.";
191
+ case "STORAGE_UPLOAD_FAILED":
192
+ return "File upload failed. Please try again.";
193
+ case "STORAGE_NOT_FOUND":
194
+ return "File not found.";
195
+ case "EMAIL_SEND_FAILED":
196
+ return "Email could not be sent. Please try again.";
197
+ case "EMAIL_NOT_CONFIGURED":
198
+ return "Email is not set up for this app.";
199
+ case "PAYMENTS_NOT_CONFIGURED":
200
+ return "Payments are not configured. Contact the developer.";
201
+ case "PAYMENTS_FAILED":
202
+ return "Payment processing failed. Please try again.";
203
+ case "NOT_FOUND":
204
+ return "The requested resource was not found.";
205
+ case "VALIDATION_ERROR":
206
+ return "Invalid input. Please check your data and try again.";
207
+ default:
208
+ return "Something went wrong. We're looking into it.";
209
+ }
210
+ }
211
+ toString() {
212
+ return `${this.name} [${this.errorCode ?? this.code}]: ${this.message}`;
213
+ }
214
+ toJSON() {
215
+ return {
216
+ name: this.name,
217
+ code: this.code,
218
+ errorCode: this.errorCode,
219
+ message: this.message,
220
+ capability: this.capability,
221
+ retryable: this.retryable,
222
+ requestId: this.requestId,
223
+ hint: this.hint,
224
+ component: this.component
225
+ };
226
+ }
17
227
  };
18
- var __copyProps = (to, from, except, desc) => {
19
- if (from && typeof from === "object" || typeof from === "function") {
20
- for (let key of __getOwnPropNames(from))
21
- if (!__hasOwnProp.call(to, key) && key !== except)
22
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
228
+ var BrokrAuthError = class extends BrokrError {
229
+ constructor(message, code) {
230
+ super(message, code, "auth", false);
231
+ this.name = "BrokrAuthError";
23
232
  }
24
- return to;
25
233
  };
26
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
-
28
- // src/auth.ts
29
- var auth_exports = {};
30
- __export(auth_exports, {
31
- BrokrAuthClient: () => BrokrAuthClient,
32
- authMiddleware: () => authMiddleware
33
- });
34
- function parseCookies(cookieHeader) {
35
- const cookies = /* @__PURE__ */ new Map();
36
- for (const pair of cookieHeader.split(";")) {
37
- const eqIdx = pair.indexOf("=");
38
- if (eqIdx === -1) continue;
39
- const name = pair.slice(0, eqIdx).trim();
40
- const value = pair.slice(eqIdx + 1).trim();
41
- if (name) cookies.set(name, value);
234
+ var BrokrRateLimitError = class extends BrokrError {
235
+ constructor(message, retryAfter, capability) {
236
+ super(message, "RATE_LIMITED", capability, true);
237
+ this.retryAfter = retryAfter;
238
+ this.name = "BrokrRateLimitError";
42
239
  }
43
- return cookies;
44
- }
45
- function authMiddleware(options) {
46
- const { protectedRoutes = [], publicOnlyRoutes = [] } = options;
47
- return async function middleware(request) {
48
- const url = new URL(request.url);
49
- const pathname = url.pathname;
50
- const isProtected = protectedRoutes.some((route) => pathname.startsWith(route));
51
- const isPublicOnly = publicOnlyRoutes.some((route) => pathname.startsWith(route));
52
- if (!isProtected && !isPublicOnly) return void 0;
53
- const cookieHeader = request.headers.get("cookie") ?? "";
54
- const cookies = parseCookies(cookieHeader);
55
- const hasSession = cookies.has("better-auth.session_token");
56
- if (isProtected && !hasSession) {
57
- return Response.redirect(new URL("/sign-in", request.url).toString(), 302);
58
- }
59
- if (isPublicOnly && hasSession) {
60
- return Response.redirect(new URL("/", request.url).toString(), 302);
61
- }
62
- return void 0;
63
- };
64
- }
65
- var BrokrAuthClient;
66
- var init_auth = __esm({
67
- "src/auth.ts"() {
68
- "use strict";
69
- init_runtime();
70
- BrokrAuthClient = class {
71
- constructor(token, gatewayUrl, appUrl) {
72
- this._token = token;
73
- this._gatewayUrl = gatewayUrl;
74
- this._appUrl = appUrl ?? (typeof process !== "undefined" ? process.env.BETTER_AUTH_URL : void 0);
75
- }
76
- /**
77
- * Get current user from an incoming request's cookies.
78
- * Calls the local Better Auth API (runs inside your app).
79
- */
80
- async currentUser(request) {
81
- const session = await this.getSession(request);
82
- return session?.user ?? null;
83
- }
84
- /**
85
- * Get the full session (user + metadata) from an incoming request.
86
- * Calls the local Better Auth API.
87
- */
88
- async getSession(request) {
89
- const appUrl = this._appUrl;
90
- if (!appUrl) {
91
- throw new BrokrError(
92
- "[brokr] BETTER_AUTH_URL is not set. Auth may not be provisioned.",
93
- "AUTH_NOT_CONFIGURED",
94
- "auth"
95
- );
96
- }
97
- const cookieHeader = request.headers.get("cookie") ?? "";
98
- if (!cookieHeader) return null;
99
- const res = await fetch(`${appUrl}/api/auth/get-session`, {
100
- method: "GET",
101
- headers: { cookie: cookieHeader }
102
- });
103
- if (!res.ok) return null;
104
- const data = await res.json();
105
- if (!data?.user || !data?.session) return null;
106
- return {
107
- user: {
108
- id: data.user.id,
109
- email: data.user.email,
110
- name: data.user.name ?? null,
111
- avatarUrl: data.user.image ?? null,
112
- emailVerified: data.user.emailVerified ? /* @__PURE__ */ new Date() : null
113
- },
114
- sessionId: data.session.id,
115
- expiresAt: new Date(data.session.expiresAt)
116
- };
117
- }
118
- /**
119
- * Generate an OAuth authorization URL.
120
- */
121
- async getOAuthUrl(provider, options) {
122
- const appUrl = this._appUrl;
123
- if (!appUrl) {
124
- throw new BrokrError("[brokr] BETTER_AUTH_URL is not set.", "AUTH_NOT_CONFIGURED", "auth");
125
- }
126
- const redirectTo = options?.redirectTo ?? "/";
127
- if (!redirectTo.startsWith("/") || redirectTo.startsWith("//")) {
128
- throw new BrokrError("[brokr] redirectTo must be a relative path (start with /)", "INVALID_REDIRECT", "auth");
129
- }
130
- const params = new URLSearchParams({ callbackURL: redirectTo });
131
- return { redirectUrl: `${appUrl}/api/auth/sign-in/social?provider=${provider}&${params}` };
132
- }
133
- /**
134
- * Send a magic link email (requires email capability).
135
- */
136
- async sendMagicLink(email, options) {
137
- const appUrl = this._appUrl;
138
- if (!appUrl) {
139
- throw new BrokrError("[brokr] BETTER_AUTH_URL is not set.", "AUTH_NOT_CONFIGURED", "auth");
140
- }
141
- const res = await fetch(`${appUrl}/api/auth/magic-link/send`, {
142
- method: "POST",
143
- headers: { "Content-Type": "application/json" },
144
- body: JSON.stringify({ email, callbackURL: options?.redirectTo ?? "/" })
145
- });
146
- if (!res.ok) {
147
- const data = await res.json().catch(() => ({}));
148
- throw new BrokrError(
149
- data.message ?? `[brokr] Failed to send magic link (HTTP ${res.status})`,
150
- "AUTH_MAGIC_LINK_FAILED",
151
- "auth"
152
- );
153
- }
154
- }
155
- };
240
+ };
241
+ var BrokrNetworkError = class extends BrokrError {
242
+ constructor(message, capability) {
243
+ super(message, "NETWORK_ERROR", capability, true);
244
+ this.name = "BrokrNetworkError";
156
245
  }
157
- });
246
+ };
247
+ var BrokrTimeoutError = class extends BrokrError {
248
+ constructor(message, capability) {
249
+ super(message, "TIMEOUT", capability, true);
250
+ this.name = "BrokrTimeoutError";
251
+ }
252
+ };
253
+ var BrokrNotFoundError = class extends BrokrError {
254
+ constructor(message, capability) {
255
+ super(message, "NOT_FOUND", capability, false);
256
+ this.name = "BrokrNotFoundError";
257
+ }
258
+ };
259
+ var BrokrValidationError = class extends BrokrError {
260
+ constructor(message, capability) {
261
+ super(message, "VALIDATION_ERROR", capability, false);
262
+ this.name = "BrokrValidationError";
263
+ }
264
+ };
158
265
 
159
- // src/runtime.ts
266
+ // src/gateway.ts
267
+ var GATEWAY_URL = "https://api.brokr.sh";
268
+ var FETCH_TIMEOUT_MS = 3e4;
160
269
  function resolveToken() {
161
270
  return typeof process !== "undefined" ? process.env.BROKR_TOKEN : void 0;
162
271
  }
163
- function assertToken(token, capability) {
272
+ function requireToken(token, capability) {
164
273
  if (!token) {
165
274
  let hint = "brokr env pull --stack <name>";
166
275
  try {
@@ -176,326 +285,1905 @@ function assertToken(token, capability) {
176
285
  } catch {
177
286
  }
178
287
  throw new BrokrAuthError(
179
- `[brokr] BROKR_TOKEN is not set.
180
- Run: ${hint}`,
288
+ `BROKR_TOKEN is not set. Run: ${hint}`,
181
289
  "BROKR_TOKEN_MISSING"
182
290
  );
183
291
  }
184
292
  }
185
293
  async function gatewayFetch(gatewayUrl, token, path, body, capability) {
294
+ const controller = new AbortController();
295
+ const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
186
296
  let res;
187
297
  try {
188
298
  res = await fetch(`${gatewayUrl}${path}`, {
189
299
  method: "POST",
190
300
  headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
191
- body: JSON.stringify(body)
301
+ body: JSON.stringify(body),
302
+ signal: controller.signal
192
303
  });
193
304
  } catch (err) {
305
+ if (err instanceof Error && err.name === "AbortError") {
306
+ throw new BrokrTimeoutError(
307
+ `Request timed out after ${FETCH_TIMEOUT_MS / 1e3}s`,
308
+ capability
309
+ );
310
+ }
194
311
  throw new BrokrNetworkError(
195
- `[brokr] Could not reach Brokr gateway. Check your network.
196
- ${err instanceof Error ? err.message : String(err)}`,
312
+ "Could not reach Brokr gateway. Check your network.",
197
313
  capability
198
314
  );
315
+ } finally {
316
+ clearTimeout(timeout);
199
317
  }
200
318
  if (res.status === 429) {
201
- const retryAfter = parseInt(res.headers.get("Retry-After") ?? "60", 10);
319
+ const rawRetryAfter = parseInt(res.headers.get("Retry-After") ?? "60", 10);
320
+ const retryAfter = Number.isFinite(rawRetryAfter) ? rawRetryAfter : 60;
202
321
  const data = await res.json().catch(() => ({}));
322
+ const errorMsg = typeof data.error === "string" ? data.error : `Rate limited (retry after ${retryAfter}s)`;
203
323
  throw new BrokrRateLimitError(
204
- data.error ?? `[brokr] Rate limited (retry after ${retryAfter}s)`,
324
+ errorMsg,
205
325
  retryAfter,
206
326
  capability
207
327
  );
208
328
  }
209
329
  if (res.status === 401) {
210
- throw new BrokrAuthError("[brokr] Invalid or expired BROKR_TOKEN.", "BROKR_TOKEN_INVALID");
330
+ throw new BrokrAuthError("Invalid or expired BROKR_TOKEN.", "BROKR_TOKEN_INVALID");
211
331
  }
212
332
  if (!res.ok) {
213
- const data = await res.json().catch(() => ({}));
333
+ const body2 = await res.json().catch(() => ({}));
334
+ const errObj = typeof body2.error === "object" && body2.error !== null ? body2.error : void 0;
335
+ const errorData = body2.data ?? errObj?.data ?? body2;
336
+ const errorCode = errorData.errorCode ?? body2.code;
337
+ const hint = errorData.hint;
338
+ const requestId = errorData.requestId ?? res.headers.get("x-request-id");
339
+ const retryable = errorData.retryable;
340
+ const errorStr = typeof body2.error === "string" ? body2.error : void 0;
341
+ const message = body2.message ?? errObj?.message ?? errorStr ?? `${capability} request failed (HTTP ${res.status})`;
214
342
  throw new BrokrError(
215
- data.error ?? `[brokr] ${capability} request failed (HTTP ${res.status})`,
216
- data.code ?? `${capability.toUpperCase()}_FAILED`,
343
+ message,
344
+ errorCode ?? `${capability.toUpperCase()}_FAILED`,
345
+ capability,
346
+ retryable ?? false,
347
+ errorCode ?? void 0,
348
+ requestId ?? void 0,
349
+ hint ?? void 0
350
+ );
351
+ }
352
+ try {
353
+ return await res.json();
354
+ } catch {
355
+ throw new BrokrError(
356
+ `${capability} returned invalid response (expected JSON).`,
357
+ `${capability.toUpperCase()}_INVALID_RESPONSE`,
358
+ capability,
359
+ true
360
+ );
361
+ }
362
+ }
363
+ async function gatewayStream(gatewayUrl, token, path, body, capability) {
364
+ const controller = new AbortController();
365
+ const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
366
+ let res;
367
+ try {
368
+ res = await fetch(`${gatewayUrl}${path}`, {
369
+ method: "POST",
370
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
371
+ body: JSON.stringify(body),
372
+ signal: controller.signal
373
+ });
374
+ } catch (err) {
375
+ clearTimeout(timeout);
376
+ if (err instanceof Error && err.name === "AbortError") {
377
+ throw new BrokrTimeoutError(
378
+ `Stream request timed out after ${FETCH_TIMEOUT_MS / 1e3}s`,
379
+ capability
380
+ );
381
+ }
382
+ throw new BrokrNetworkError(
383
+ "Could not reach Brokr gateway. Check your network.",
217
384
  capability
218
385
  );
219
386
  }
220
- return res.json();
387
+ clearTimeout(timeout);
388
+ if (res.status === 429) {
389
+ const retryAfter = parseInt(res.headers.get("Retry-After") ?? "60", 10);
390
+ throw new BrokrRateLimitError("Rate limited.", retryAfter, capability);
391
+ }
392
+ if (res.status === 401) {
393
+ throw new BrokrAuthError("Invalid or expired BROKR_TOKEN.", "BROKR_TOKEN_INVALID");
394
+ }
395
+ if (!res.ok || !res.body) {
396
+ throw new BrokrError(
397
+ `${capability} stream failed (HTTP ${res.status})`,
398
+ `${capability.toUpperCase()}_STREAM_FAILED`,
399
+ capability
400
+ );
401
+ }
402
+ return res;
221
403
  }
222
- function createBrokr(options) {
223
- return new BrokrRuntime(options);
404
+
405
+ // src/env-detect.ts
406
+ var cached = null;
407
+ function detectEnv() {
408
+ if (cached) return cached;
409
+ cached = _detect();
410
+ return cached;
224
411
  }
225
- var GATEWAY_URL, BrokrError, BrokrAuthError, BrokrRateLimitError, BrokrNetworkError, models, BrokrAIClient, BrokrStorageClient, BrokrEmailClient, BrokrRuntime;
226
- var init_runtime = __esm({
227
- "src/runtime.ts"() {
228
- GATEWAY_URL = "https://api.brokr.sh";
229
- BrokrError = class extends Error {
230
- constructor(message, code, capability, retryable = false) {
231
- super(message);
232
- this.code = code;
233
- this.capability = capability;
234
- this.retryable = retryable;
235
- this.name = "BrokrError";
236
- }
237
- };
238
- BrokrAuthError = class extends BrokrError {
239
- constructor(message, code) {
240
- super(message, code, "auth", false);
241
- this.name = "BrokrAuthError";
242
- }
243
- };
244
- BrokrRateLimitError = class extends BrokrError {
245
- constructor(message, retryAfter, capability) {
246
- super(message, "RATE_LIMITED", capability, true);
247
- this.retryAfter = retryAfter;
248
- this.name = "BrokrRateLimitError";
412
+ function isDev() {
413
+ return detectEnv() === "development";
414
+ }
415
+ function isStaging() {
416
+ return detectEnv() === "staging";
417
+ }
418
+ function isProd() {
419
+ return detectEnv() === "production";
420
+ }
421
+ function _detect() {
422
+ if (typeof process !== "undefined" && process.env) {
423
+ const vercel = process.env.VERCEL;
424
+ const vercelEnv = process.env.VERCEL_ENV;
425
+ if (vercel === "1" || vercel === "true") {
426
+ if (vercelEnv === "preview") return "staging";
427
+ if (vercelEnv === "production") return "production";
428
+ return "production";
429
+ }
430
+ return "development";
431
+ }
432
+ if (typeof window !== "undefined" && window.location) {
433
+ const host = window.location.hostname;
434
+ if (host === "localhost" || host === "127.0.0.1" || host === "::1") {
435
+ return "development";
436
+ }
437
+ if (host.includes("-staging.brokr.sh") || host.includes(".preview.brokr.sh")) {
438
+ return "staging";
439
+ }
440
+ if (host.endsWith(".brokr.sh") || host === "brokr.sh") {
441
+ return "production";
442
+ }
443
+ return "production";
444
+ }
445
+ return "production";
446
+ }
447
+
448
+ // src/dev-console.ts
449
+ var BrokrDevConsole = class _BrokrDevConsole {
450
+ static {
451
+ this.installed = false;
452
+ }
453
+ static {
454
+ this.env = "production";
455
+ }
456
+ static install() {
457
+ if (_BrokrDevConsole.installed) return;
458
+ const env = detectEnv();
459
+ if (env === "production") return;
460
+ _BrokrDevConsole.installed = true;
461
+ _BrokrDevConsole.env = env;
462
+ if (typeof process !== "undefined" && process.on) {
463
+ process.on("unhandledRejection", (err) => {
464
+ if (err instanceof BrokrError) {
465
+ try {
466
+ _BrokrDevConsole.print(err);
467
+ } catch {
468
+ }
469
+ }
470
+ });
471
+ }
472
+ }
473
+ static print(err) {
474
+ if (_BrokrDevConsole.env === "staging") {
475
+ const hint = err.hint ?? err.message;
476
+ console.error(`[brokr] ${err.errorCode ?? err.code}: ${hint}`);
477
+ return;
478
+ }
479
+ const lines = [
480
+ "",
481
+ "\u2501".repeat(41),
482
+ "",
483
+ ` Brokr${err.component ? ` \u2014 ${err.component}` : ""}`,
484
+ "",
485
+ ` ${err.message}`
486
+ ];
487
+ if (err.fix) {
488
+ lines.push("", " Fix it:");
489
+ for (const line of err.fix.split("\n")) lines.push(` ${line}`);
490
+ }
491
+ if (err.docsUrl) {
492
+ lines.push("", ` Still broken? \u2192 ${err.docsUrl}`);
493
+ }
494
+ lines.push("", "\u2501".repeat(41), "");
495
+ console.error(lines.join("\n"));
496
+ }
497
+ };
498
+
499
+ // src/logs/capture.ts
500
+ var DEFAULT_BATCH_SIZE = 20;
501
+ var DEFAULT_FLUSH_INTERVAL_MS = 5e3;
502
+ var buffer = [];
503
+ var flushTimer = null;
504
+ var config = null;
505
+ var intercepted = false;
506
+ function initCapture(opts = {}) {
507
+ if (config) return;
508
+ const token = opts.token ?? (typeof process !== "undefined" ? process.env.BROKR_TOKEN : void 0);
509
+ const stackId = opts.stackId ?? resolveStackId();
510
+ if (!token || !stackId) {
511
+ if (typeof process !== "undefined") {
512
+ console.log(`[brokr] Log capture skipped: token=${token ? "present" : "MISSING"} stackId=${stackId ?? "MISSING"}`);
513
+ }
514
+ return;
515
+ }
516
+ const apiUrl = opts.apiUrl ?? opts.gatewayUrl ?? (typeof process !== "undefined" ? process.env.BROKR_GATEWAY_URL : void 0) ?? "https://api.brokr.sh";
517
+ config = {
518
+ token,
519
+ stackId,
520
+ apiUrl: apiUrl.replace(/\/+$/, ""),
521
+ batchSize: opts.batchSize ?? DEFAULT_BATCH_SIZE,
522
+ flushIntervalMs: opts.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS
523
+ };
524
+ if (typeof process !== "undefined") {
525
+ console.log(`[brokr] Log capture initialized \u2192 ${apiUrl}/v1/logs/ingest (stack: ${stackId.slice(0, 8)}...)`);
526
+ }
527
+ if (flushTimer) clearInterval(flushTimer);
528
+ flushTimer = setInterval(() => flush(), config.flushIntervalMs);
529
+ if (typeof process !== "undefined" && process.on) {
530
+ process.on("beforeExit", () => flush());
531
+ }
532
+ if (typeof process !== "undefined" && !intercepted) {
533
+ intercepted = true;
534
+ const origError = console.error;
535
+ const origWarn = console.warn;
536
+ console.error = (...args) => {
537
+ origError.apply(console, args);
538
+ try {
539
+ const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
540
+ if (!msg.startsWith("[brokr]")) capture("error", msg, "console.error");
541
+ } catch {
249
542
  }
250
543
  };
251
- BrokrNetworkError = class extends BrokrError {
252
- constructor(message, capability) {
253
- super(message, "NETWORK_ERROR", capability, true);
254
- this.name = "BrokrNetworkError";
544
+ console.warn = (...args) => {
545
+ origWarn.apply(console, args);
546
+ try {
547
+ const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
548
+ if (!msg.startsWith("[brokr]")) capture("warn", msg, "console.warn");
549
+ } catch {
255
550
  }
256
551
  };
257
- models = {
258
- /** Cheapest and fastest model (Deepseek Chat). */
259
- FAST: "deepseek-chat",
260
- /** Most capable model. */
261
- SMART: "claude-sonnet-4-20250514",
262
- /** Default balanced model (Deepseek Chat). */
263
- BALANCED: "deepseek-chat"
264
- };
265
- BrokrAIClient = class {
266
- constructor(_token, _gatewayUrl) {
267
- this._token = _token;
268
- this._gatewayUrl = _gatewayUrl;
269
- }
270
- /**
271
- * Send a chat completion request.
272
- *
273
- * @example
274
- * ```typescript
275
- * const reply = await brokr.ai.chat([
276
- * { role: 'user', content: 'Explain quantum computing in one sentence.' }
277
- * ]);
278
- * console.log(reply.content);
279
- * ```
280
- */
281
- async chat(messages, options) {
282
- assertToken(this._token, "ai");
283
- const data = await gatewayFetch(this._gatewayUrl, this._token, "/v1/chat/completions", {
552
+ }
553
+ }
554
+ function capture(level, message, source, stackTrace) {
555
+ if (!config) return;
556
+ buffer.push({
557
+ level,
558
+ message: message.slice(0, 1e4),
559
+ source: source ?? "app",
560
+ stackTrace: stackTrace?.slice(0, 5e4),
561
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
562
+ });
563
+ if (buffer.length >= (config.batchSize ?? DEFAULT_BATCH_SIZE)) {
564
+ flush();
565
+ }
566
+ }
567
+ function flush() {
568
+ if (!config || buffer.length === 0) return;
569
+ const entries = buffer.splice(0);
570
+ const { token, stackId, apiUrl } = config;
571
+ fetch(`${apiUrl}/v1/logs/ingest`, {
572
+ method: "POST",
573
+ headers: {
574
+ "Content-Type": "application/json",
575
+ Authorization: `Bearer ${token}`
576
+ },
577
+ body: JSON.stringify({ stackId, entries })
578
+ }).catch((err) => {
579
+ if (typeof console !== "undefined") {
580
+ console.warn("[brokr] Log flush failed:", err instanceof Error ? err.message : err);
581
+ }
582
+ });
583
+ }
584
+ function resolveStackId() {
585
+ if (typeof process === "undefined") return void 0;
586
+ if (process.env.BROKR_STACK_ID) return process.env.BROKR_STACK_ID;
587
+ try {
588
+ const fs = __require("fs");
589
+ const path = __require("path");
590
+ const brokrFile = path.join(process.cwd(), ".brokr");
591
+ if (fs.existsSync(brokrFile)) {
592
+ const content = fs.readFileSync(brokrFile, "utf8");
593
+ const match = content.match(/BROKR_STACK_ID=(.+)/);
594
+ if (match) return match[1].trim();
595
+ }
596
+ } catch {
597
+ }
598
+ return void 0;
599
+ }
600
+
601
+ // src/ai/client.ts
602
+ function normalizeInput(input) {
603
+ if (typeof input === "string") {
604
+ return [{ role: "user", content: input }];
605
+ }
606
+ return input;
607
+ }
608
+ var BrokrAIClient = class {
609
+ constructor(_token, _gatewayUrl) {
610
+ this._token = _token;
611
+ this._gatewayUrl = _gatewayUrl;
612
+ }
613
+ // ---------------------------------------------------------------------------
614
+ // Core chat
615
+ // ---------------------------------------------------------------------------
616
+ /**
617
+ * Send a chat completion request.
618
+ * Accepts a string (auto-wrapped as user message) or a message array.
619
+ */
620
+ async chat(input, options) {
621
+ requireToken(this._token, "ai");
622
+ const messages = normalizeInput(input);
623
+ try {
624
+ const data = await gatewayFetch(this._gatewayUrl, this._token, "/v1/chat/completions", {
625
+ messages,
626
+ model: options?.model,
627
+ max_tokens: options?.maxTokens,
628
+ temperature: options?.temperature
629
+ }, "ai");
630
+ return {
631
+ content: data.choices?.[0]?.message?.content ?? "",
632
+ model: data.model ?? "",
633
+ usage: {
634
+ promptTokens: data.usage?.prompt_tokens ?? 0,
635
+ completionTokens: data.usage?.completion_tokens ?? 0
636
+ }
637
+ };
638
+ } catch (err) {
639
+ if (err instanceof BrokrError) err.component = "AIChat";
640
+ const msg = err instanceof Error ? err.message : String(err);
641
+ capture("error", `AI chat failed: ${msg}`, "brokr.ai.chat", err instanceof Error ? err.stack : void 0);
642
+ throw err;
643
+ }
644
+ }
645
+ /**
646
+ * Stream a chat completion. Yields text strings directly.
647
+ * Accepts a string (auto-wrapped as user message) or a message array.
648
+ */
649
+ /**
650
+ * Stream a chat completion. Yields text strings directly.
651
+ * Tries streaming first — if the gateway or provider doesn't support it,
652
+ * falls back to a non-streaming call and yields the full response at once.
653
+ */
654
+ async *stream(input, options) {
655
+ requireToken(this._token, "ai");
656
+ const messages = normalizeInput(input);
657
+ let res;
658
+ try {
659
+ res = await gatewayStream(
660
+ this._gatewayUrl,
661
+ this._token,
662
+ "/v1/chat/completions",
663
+ {
284
664
  messages,
665
+ stream: true,
285
666
  model: options?.model,
286
667
  max_tokens: options?.maxTokens,
287
668
  temperature: options?.temperature
288
- }, "ai");
289
- return {
290
- content: data.choices?.[0]?.message?.content ?? "",
291
- model: data.model ?? "",
292
- usage: {
293
- promptTokens: data.usage?.prompt_tokens ?? 0,
294
- completionTokens: data.usage?.completion_tokens ?? 0
295
- }
296
- };
669
+ },
670
+ "ai"
671
+ );
672
+ } catch (err) {
673
+ if (err instanceof BrokrError) err.component = "AIStream";
674
+ const msg = err instanceof Error ? err.message : String(err);
675
+ capture("error", `AI stream failed: ${msg}`, "brokr.ai.stream", err instanceof Error ? err.stack : void 0);
676
+ try {
677
+ const fallback = await this.chat(input, options);
678
+ yield fallback.content;
679
+ } catch (fallbackErr) {
680
+ throw err;
297
681
  }
298
- /**
299
- * Stream a chat completion. Yields text strings directly.
300
- *
301
- * @example
302
- * ```typescript
303
- * for await (const text of brokr.ai.stream(messages)) {
304
- * process.stdout.write(text);
305
- * }
306
- * ```
307
- */
308
- async *stream(messages, options) {
309
- assertToken(this._token, "ai");
310
- let res;
682
+ return;
683
+ }
684
+ const ct = res.headers.get("content-type") ?? "";
685
+ if (!ct.includes("text/event-stream")) {
686
+ try {
687
+ const data = await res.json();
688
+ const content = data.choices?.[0]?.message?.content ?? "";
689
+ if (content) yield content;
690
+ } catch {
691
+ const fallback = await this.chat(input, options);
692
+ yield fallback.content;
693
+ }
694
+ return;
695
+ }
696
+ const reader = res.body.getReader();
697
+ const decoder = new TextDecoder();
698
+ let buffer2 = "";
699
+ while (true) {
700
+ const { done, value } = await reader.read();
701
+ if (done) break;
702
+ buffer2 += decoder.decode(value, { stream: true });
703
+ const lines = buffer2.split("\n");
704
+ buffer2 = lines.pop() ?? "";
705
+ for (const line of lines) {
706
+ if (!line.startsWith("data: ")) continue;
707
+ const payload = line.slice(6).trim();
708
+ if (payload === "[DONE]") return;
311
709
  try {
312
- res = await fetch(`${this._gatewayUrl}/v1/chat/completions`, {
313
- method: "POST",
314
- headers: { "Content-Type": "application/json", Authorization: `Bearer ${this._token}` },
315
- body: JSON.stringify({ messages, stream: true, model: options?.model, max_tokens: options?.maxTokens })
316
- });
317
- } catch (err) {
318
- throw new BrokrNetworkError(
319
- `[brokr] Could not reach Brokr gateway.
320
- ${err instanceof Error ? err.message : String(err)}`,
321
- "ai"
322
- );
323
- }
324
- if (res.status === 429) {
325
- const retryAfter = parseInt(res.headers.get("Retry-After") ?? "60", 10);
326
- throw new BrokrRateLimitError("[brokr] AI rate limited.", retryAfter, "ai");
327
- }
328
- if (res.status === 401) {
329
- throw new BrokrAuthError("[brokr] Invalid or expired BROKR_TOKEN.", "BROKR_TOKEN_INVALID");
330
- }
331
- if (!res.ok || !res.body) {
332
- throw new BrokrError(`[brokr] AI stream failed (HTTP ${res.status})`, "AI_STREAM_FAILED", "ai");
333
- }
334
- const reader = res.body.getReader();
335
- const decoder = new TextDecoder();
336
- let buffer = "";
337
- while (true) {
338
- const { done, value } = await reader.read();
339
- if (done) break;
340
- buffer += decoder.decode(value, { stream: true });
341
- const lines = buffer.split("\n");
342
- buffer = lines.pop() ?? "";
343
- for (const line of lines) {
344
- if (!line.startsWith("data: ")) continue;
345
- const payload = line.slice(6).trim();
346
- if (payload === "[DONE]") return;
347
- try {
348
- const parsed = JSON.parse(payload);
349
- const delta = parsed.choices?.[0]?.delta?.content ?? "";
350
- if (delta) yield delta;
351
- } catch {
352
- }
353
- }
710
+ const parsed = JSON.parse(payload);
711
+ const delta = parsed.choices?.[0]?.delta?.content ?? "";
712
+ if (delta) yield delta;
713
+ } catch {
354
714
  }
355
715
  }
356
- /**
357
- * OpenAI-SDK compatible base URL.
358
- *
359
- * @example
360
- * ```typescript
361
- * const openai = new OpenAI({ baseURL: brokr.ai.baseURL, apiKey: brokr.ai.apiKey });
362
- * ```
363
- */
364
- get baseURL() {
365
- return `${this._gatewayUrl}/v1`;
716
+ }
717
+ }
718
+ // ---------------------------------------------------------------------------
719
+ // Higher-level primitives
720
+ // ---------------------------------------------------------------------------
721
+ /**
722
+ * Extract structured data from a prompt using JSON mode.
723
+ * Returns a parsed object matching the provided schema shape.
724
+ */
725
+ async structured(params) {
726
+ requireToken(this._token, "ai");
727
+ const schemaStr = JSON.stringify(params.schema, null, 2);
728
+ const messages = [
729
+ {
730
+ role: "system",
731
+ content: `You are a structured data extraction assistant. Return ONLY valid JSON matching this schema:
732
+ ${schemaStr}
733
+ Do not include any other text, markdown, or explanation.`
734
+ },
735
+ { role: "user", content: params.prompt }
736
+ ];
737
+ try {
738
+ const data = await gatewayFetch(this._gatewayUrl, this._token, "/v1/chat/completions", {
739
+ messages,
740
+ model: params.model,
741
+ temperature: params.temperature ?? 0,
742
+ response_format: { type: "json_object" }
743
+ }, "ai");
744
+ const raw = data.choices?.[0]?.message?.content ?? "{}";
745
+ try {
746
+ return JSON.parse(raw);
747
+ } catch {
748
+ throw new BrokrError(
749
+ "[brokr] AI returned invalid JSON for structured extraction.",
750
+ "AI_STRUCTURED_PARSE_ERROR",
751
+ "ai"
752
+ );
366
753
  }
367
- /** Use as `apiKey` with the official OpenAI SDK to route through Brokr's gateway. */
368
- get apiKey() {
369
- assertToken(this._token, "ai");
370
- return this._token;
754
+ } catch (err) {
755
+ if (err instanceof BrokrError) err.component = "AIStructured";
756
+ throw err;
757
+ }
758
+ }
759
+ /**
760
+ * Extract fields from unstructured text.
761
+ * Semantic alias for structured() — same behavior, clearer intent.
762
+ */
763
+ async extract(prompt, schema) {
764
+ return this.structured({ prompt, schema });
765
+ }
766
+ /**
767
+ * Summarize text using AI.
768
+ */
769
+ async summarize(text, options) {
770
+ try {
771
+ const lengthHint = options?.maxLength ? ` Keep it under ${options.maxLength}.` : "";
772
+ const response = await this.chat(
773
+ [
774
+ {
775
+ role: "system",
776
+ content: `You are a summarization assistant. Provide a concise, accurate summary of the following text.${lengthHint} Return only the summary, no preamble.`
777
+ },
778
+ { role: "user", content: text }
779
+ ],
780
+ { model: options?.model }
781
+ );
782
+ return { summary: response.content };
783
+ } catch (err) {
784
+ if (err instanceof BrokrError) err.component = "AISummarize";
785
+ throw err;
786
+ }
787
+ }
788
+ /**
789
+ * Classify text into one of the provided labels.
790
+ */
791
+ async classify(text, labels, options) {
792
+ try {
793
+ const labelsStr = labels.map((l) => `"${l}"`).join(", ");
794
+ const result = await this.structured({
795
+ prompt: `Classify the following text into exactly one of these labels: [${labelsStr}]
796
+
797
+ Text: ${text}`,
798
+ schema: {
799
+ type: "object",
800
+ properties: {
801
+ label: { type: "string", enum: labels },
802
+ confidence: { type: "number", minimum: 0, maximum: 1 }
803
+ },
804
+ required: ["label", "confidence"]
805
+ },
806
+ model: options?.model,
807
+ temperature: 0
808
+ });
809
+ return result;
810
+ } catch (err) {
811
+ if (err instanceof BrokrError) err.component = "AIClassify";
812
+ throw err;
813
+ }
814
+ }
815
+ /**
816
+ * Generate an embedding vector for the given text.
817
+ */
818
+ async embed(text) {
819
+ requireToken(this._token, "ai");
820
+ try {
821
+ const data = await gatewayFetch(this._gatewayUrl, this._token, "/v1/embeddings", {
822
+ input: text,
823
+ model: "text-embedding-3-small"
824
+ }, "ai");
825
+ return {
826
+ vector: data.data?.[0]?.embedding ?? []
827
+ };
828
+ } catch (err) {
829
+ if (err instanceof BrokrError) err.component = "AIEmbed";
830
+ throw err;
831
+ }
832
+ }
833
+ /**
834
+ * Generate an image from a text prompt.
835
+ */
836
+ async image(prompt, options) {
837
+ requireToken(this._token, "ai");
838
+ try {
839
+ const data = await gatewayFetch(this._gatewayUrl, this._token, "/v1/images/generate", {
840
+ prompt,
841
+ size: options?.size ?? "1024x1024",
842
+ n: options?.n ?? 1,
843
+ model: options?.model
844
+ }, "ai");
845
+ const url = data.data?.[0]?.url;
846
+ if (!url) {
847
+ throw new BrokrError("[brokr] Image generation returned no URL.", "AI_IMAGE_FAILED", "ai");
371
848
  }
372
- };
373
- BrokrStorageClient = class {
374
- constructor(_token, _gatewayUrl) {
375
- this._token = _token;
376
- this._gatewayUrl = _gatewayUrl;
849
+ return { url };
850
+ } catch (err) {
851
+ if (err instanceof BrokrError) err.component = "AIImage";
852
+ throw err;
853
+ }
854
+ }
855
+ // ---------------------------------------------------------------------------
856
+ // OpenAI-SDK compatibility
857
+ // ---------------------------------------------------------------------------
858
+ /** OpenAI-SDK compatible base URL. */
859
+ get baseURL() {
860
+ return `${this._gatewayUrl}/v1`;
861
+ }
862
+ /** Use as `apiKey` with the official OpenAI SDK to route through Brokr's gateway. */
863
+ get apiKey() {
864
+ requireToken(this._token, "ai");
865
+ return this._token;
866
+ }
867
+ };
868
+
869
+ // src/storage/client.ts
870
+ var MULTIPART_THRESHOLD = 100 * 1024 * 1024;
871
+ var DEFAULT_PART_SIZE = 100 * 1024 * 1024;
872
+ var PART_UPLOAD_RETRIES = 3;
873
+ var BrokrStorageClient = class {
874
+ constructor(_token, _gatewayUrl) {
875
+ this._token = _token;
876
+ this._gatewayUrl = _gatewayUrl;
877
+ }
878
+ async upload(paramsOrData, filename, contentTypeArg) {
879
+ try {
880
+ let data;
881
+ let filePath;
882
+ let contentType;
883
+ if (typeof paramsOrData === "object" && paramsOrData !== null && "file" in paramsOrData) {
884
+ data = paramsOrData.file;
885
+ filePath = paramsOrData.path ?? `upload-${Date.now()}`;
886
+ contentType = paramsOrData.contentType ?? "application/octet-stream";
887
+ } else {
888
+ data = paramsOrData;
889
+ filePath = filename ?? `upload-${Date.now()}`;
890
+ contentType = contentTypeArg ?? "application/octet-stream";
377
891
  }
378
- /**
379
- * Get a presigned upload URL for browser-direct or streaming uploads.
380
- *
381
- * @example
382
- * ```typescript
383
- * const { url, key } = await brokr.storage.getUploadUrl('avatar.png', 'image/png');
384
- * await fetch(url, { method: 'PUT', body: file });
385
- * ```
386
- */
387
- async getUploadUrl(filename, contentType = "application/octet-stream") {
388
- assertToken(this._token, "storage");
389
- return gatewayFetch(
390
- this._gatewayUrl,
391
- this._token,
392
- "/v1/storage/sign-upload",
393
- { filename, contentType },
394
- "storage"
395
- );
892
+ const size = getUploadSize(data);
893
+ if (size !== void 0 && size > MULTIPART_THRESHOLD) {
894
+ return await this._uploadMultipart(data, filePath, contentType, size);
396
895
  }
397
- /**
398
- * Upload data to R2. Returns the stable object key.
399
- *
400
- * @example
401
- * ```typescript
402
- * const { key } = await brokr.storage.upload(fileBuffer, 'photo.jpg', 'image/jpeg');
403
- * ```
404
- */
405
- async upload(data, filename, contentType = "application/octet-stream") {
406
- const { url, key } = await this.getUploadUrl(filename, contentType);
407
- const putRes = await fetch(url, {
408
- method: "PUT",
409
- headers: { "Content-Type": contentType },
410
- body: data
411
- });
412
- if (!putRes.ok) {
413
- throw new BrokrError(`[brokr] Upload failed (HTTP ${putRes.status})`, "STORAGE_UPLOAD_FAILED", "storage");
414
- }
415
- return { key };
896
+ const { url, key } = await this.signUpload({ fileName: filePath, contentType });
897
+ const uploadHeaders = { "Content-Type": contentType };
898
+ if (typeof window === "undefined") {
899
+ const 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) ?? "http://localhost:3000";
900
+ uploadHeaders.Origin = origin;
416
901
  }
417
- /**
418
- * Get a presigned download URL for a stored object.
419
- *
420
- * @example
421
- * ```typescript
422
- * const { url } = await brokr.storage.url('photos/avatar.jpg');
423
- * // use url in <img src={url} /> or redirect
424
- * ```
425
- */
426
- async url(key, options) {
427
- assertToken(this._token, "storage");
428
- return gatewayFetch(
429
- this._gatewayUrl,
430
- this._token,
431
- "/v1/storage/sign-download",
432
- { key, expiresIn: options?.expiresIn },
902
+ const putRes = await fetch(url, {
903
+ method: "PUT",
904
+ headers: uploadHeaders,
905
+ body: data
906
+ });
907
+ if (!putRes.ok) {
908
+ throw new BrokrError(
909
+ `[brokr] Upload failed (HTTP ${putRes.status})`,
910
+ "STORAGE_UPLOAD_FAILED",
433
911
  "storage"
434
912
  );
435
913
  }
436
- /** @deprecated Use `url()` instead. */
437
- async getUrl(key, options) {
438
- return this.url(key, options);
914
+ const gatewayBase = this._gatewayUrl.replace(/\/+$/, "");
915
+ const permanentUrl = `${gatewayBase}/v1/storage/file/${encodeURI(key)}`;
916
+ return {
917
+ key,
918
+ url: permanentUrl,
919
+ name: filePath,
920
+ size,
921
+ type: contentType
922
+ };
923
+ } catch (err) {
924
+ if (err instanceof BrokrError) err.component = "Storage";
925
+ throw err;
926
+ }
927
+ }
928
+ /**
929
+ * Get a presigned download URL for a stored object.
930
+ */
931
+ async signedUrl(key, options) {
932
+ requireToken(this._token, "storage");
933
+ try {
934
+ return await gatewayFetch(
935
+ this._gatewayUrl,
936
+ this._token,
937
+ "/v1/storage/sign-download",
938
+ { key, expiresIn: options?.expiresIn },
939
+ "storage"
940
+ );
941
+ } catch (err) {
942
+ if (err instanceof BrokrError) err.component = "Storage";
943
+ throw err;
944
+ }
945
+ }
946
+ /** Low-level escape hatch. Most apps should call upload() instead. */
947
+ async signUpload(params) {
948
+ requireToken(this._token, "storage");
949
+ try {
950
+ return await gatewayFetch(
951
+ this._gatewayUrl,
952
+ this._token,
953
+ "/v1/storage/sign-upload",
954
+ { filename: params.fileName, contentType: params.contentType ?? "application/octet-stream" },
955
+ "storage"
956
+ );
957
+ } catch (err) {
958
+ if (err instanceof BrokrError) err.component = "Storage";
959
+ throw err;
960
+ }
961
+ }
962
+ // ---------------------------------------------------------------------------
963
+ // Multipart upload — handles files > 100MB, up to 20GB
964
+ // ---------------------------------------------------------------------------
965
+ /**
966
+ * Initiate a multipart upload via the gateway.
967
+ */
968
+ async _initiateMultipart(params) {
969
+ requireToken(this._token, "storage");
970
+ return gatewayFetch(
971
+ this._gatewayUrl,
972
+ this._token,
973
+ "/v1/storage/multipart/initiate",
974
+ {
975
+ filename: params.fileName,
976
+ contentType: params.contentType,
977
+ totalSize: params.totalSize,
978
+ partCount: params.partCount
979
+ },
980
+ "storage"
981
+ );
982
+ }
983
+ /**
984
+ * Get a presigned URL for uploading a single part.
985
+ */
986
+ async _signPartUpload(params) {
987
+ requireToken(this._token, "storage");
988
+ return gatewayFetch(
989
+ this._gatewayUrl,
990
+ this._token,
991
+ "/v1/storage/multipart/sign-part",
992
+ params,
993
+ "storage"
994
+ );
995
+ }
996
+ /**
997
+ * Complete a multipart upload with part ETags.
998
+ */
999
+ async _completeMultipart(params) {
1000
+ requireToken(this._token, "storage");
1001
+ return gatewayFetch(
1002
+ this._gatewayUrl,
1003
+ this._token,
1004
+ "/v1/storage/multipart/complete",
1005
+ params,
1006
+ "storage"
1007
+ );
1008
+ }
1009
+ /**
1010
+ * Abort a multipart upload, cleaning up uploaded parts.
1011
+ */
1012
+ async _abortMultipart(params) {
1013
+ requireToken(this._token, "storage");
1014
+ await gatewayFetch(
1015
+ this._gatewayUrl,
1016
+ this._token,
1017
+ "/v1/storage/multipart/abort",
1018
+ params,
1019
+ "storage"
1020
+ );
1021
+ }
1022
+ /**
1023
+ * Upload a large file using multipart upload.
1024
+ * Splits the file into parts, uploads each with retry, then completes.
1025
+ */
1026
+ async _uploadMultipart(data, filePath, contentType, totalSize) {
1027
+ const bytes = await toUint8Array(data);
1028
+ const partSize = Math.max(DEFAULT_PART_SIZE, Math.ceil(totalSize / MAX_PARTS_PER_UPLOAD));
1029
+ const partCount = Math.ceil(totalSize / partSize);
1030
+ const { uploadId, key } = await this._initiateMultipart({
1031
+ fileName: filePath,
1032
+ contentType,
1033
+ totalSize,
1034
+ partCount
1035
+ });
1036
+ try {
1037
+ const completedParts = [];
1038
+ for (let i = 0; i < partCount; i++) {
1039
+ const partNumber = i + 1;
1040
+ const start = i * partSize;
1041
+ const end = Math.min(start + partSize, totalSize);
1042
+ const partData = bytes.slice(start, end);
1043
+ const { url } = await this._signPartUpload({ key, uploadId, partNumber });
1044
+ let lastError;
1045
+ for (let attempt = 0; attempt < PART_UPLOAD_RETRIES; attempt++) {
1046
+ try {
1047
+ const putRes = await fetch(url, {
1048
+ method: "PUT",
1049
+ body: partData
1050
+ });
1051
+ if (!putRes.ok) {
1052
+ throw new Error(`Part ${partNumber} upload failed (HTTP ${putRes.status})`);
1053
+ }
1054
+ const etag = putRes.headers.get("ETag");
1055
+ if (!etag) {
1056
+ throw new Error(`Part ${partNumber} upload returned no ETag`);
1057
+ }
1058
+ completedParts.push({ partNumber, etag });
1059
+ lastError = void 0;
1060
+ break;
1061
+ } catch (err) {
1062
+ lastError = err instanceof Error ? err : new Error(String(err));
1063
+ }
1064
+ }
1065
+ if (lastError) {
1066
+ throw new BrokrError(
1067
+ `[brokr] Multipart upload failed at part ${partNumber} after ${PART_UPLOAD_RETRIES} retries: ${lastError.message}`,
1068
+ "STORAGE_UPLOAD_FAILED",
1069
+ "storage"
1070
+ );
1071
+ }
439
1072
  }
440
- };
441
- BrokrEmailClient = class {
442
- constructor(_token, _gatewayUrl) {
443
- this._token = _token;
444
- this._gatewayUrl = _gatewayUrl;
1073
+ completedParts.sort((a, b) => a.partNumber - b.partNumber);
1074
+ await this._completeMultipart({ key, uploadId, parts: completedParts });
1075
+ const gatewayBase = this._gatewayUrl.replace(/\/+$/, "");
1076
+ const permanentUrl = `${gatewayBase}/v1/storage/file/${encodeURI(key)}`;
1077
+ return {
1078
+ key,
1079
+ url: permanentUrl,
1080
+ name: filePath,
1081
+ size: totalSize,
1082
+ type: contentType
1083
+ };
1084
+ } catch (err) {
1085
+ try {
1086
+ await this._abortMultipart({ key, uploadId });
1087
+ } catch {
445
1088
  }
446
- /**
447
- * Send an email. The from address and API credentials are resolved server-side.
448
- *
449
- * @example
450
- * ```typescript
451
- * await brokr.email.send({
452
- * to: 'user@example.com',
453
- * subject: 'Welcome!',
454
- * html: '<h1>Welcome to the app</h1>',
455
- * });
456
- * ```
457
- */
458
- async send(params) {
459
- assertToken(this._token, "email");
460
- return gatewayFetch(
461
- this._gatewayUrl,
462
- this._token,
463
- "/v1/email/send",
464
- params,
1089
+ throw err;
1090
+ }
1091
+ }
1092
+ /**
1093
+ * List objects by prefix with pagination.
1094
+ */
1095
+ async list(params) {
1096
+ requireToken(this._token, "storage");
1097
+ try {
1098
+ return await gatewayFetch(
1099
+ this._gatewayUrl,
1100
+ this._token,
1101
+ "/v1/storage/list",
1102
+ {
1103
+ prefix: params?.prefix,
1104
+ maxKeys: params?.maxKeys,
1105
+ cursor: params?.cursor
1106
+ },
1107
+ "storage"
1108
+ );
1109
+ } catch (err) {
1110
+ if (err instanceof BrokrError) err.component = "Storage";
1111
+ throw err;
1112
+ }
1113
+ }
1114
+ /**
1115
+ * Delete an object by key.
1116
+ */
1117
+ async delete(key) {
1118
+ requireToken(this._token, "storage");
1119
+ try {
1120
+ await gatewayFetch(
1121
+ this._gatewayUrl,
1122
+ this._token,
1123
+ "/v1/storage/delete",
1124
+ { key },
1125
+ "storage"
1126
+ );
1127
+ } catch (err) {
1128
+ if (err instanceof BrokrError) err.component = "Storage";
1129
+ throw err;
1130
+ }
1131
+ }
1132
+ /**
1133
+ * Copy an object from one key to another.
1134
+ */
1135
+ async copy(from, to) {
1136
+ requireToken(this._token, "storage");
1137
+ try {
1138
+ return await gatewayFetch(
1139
+ this._gatewayUrl,
1140
+ this._token,
1141
+ "/v1/storage/copy",
1142
+ { from, to },
1143
+ "storage"
1144
+ );
1145
+ } catch (err) {
1146
+ if (err instanceof BrokrError) err.component = "Storage";
1147
+ throw err;
1148
+ }
1149
+ }
1150
+ /**
1151
+ * Move an object (copy then delete source).
1152
+ */
1153
+ async move(from, to) {
1154
+ try {
1155
+ const copied = await this.copy(from, to);
1156
+ await this.delete(from);
1157
+ return copied;
1158
+ } catch (err) {
1159
+ if (err instanceof BrokrError) err.component = "Storage";
1160
+ throw err;
1161
+ }
1162
+ }
1163
+ /**
1164
+ * Check if an object exists.
1165
+ */
1166
+ /**
1167
+ * Check if an object exists.
1168
+ *
1169
+ * Returns `false` on any error (not-found, rate-limit, network).
1170
+ * This preserves backwards compatibility — `if (await brokr.storage.exists(key))`
1171
+ * must never throw in user code that doesn't wrap it in try-catch.
1172
+ *
1173
+ * If you need to distinguish "doesn't exist" from "couldn't check",
1174
+ * use `metadata()` instead and catch the error.
1175
+ */
1176
+ async exists(key) {
1177
+ requireToken(this._token, "storage");
1178
+ try {
1179
+ const result = await gatewayFetch(
1180
+ this._gatewayUrl,
1181
+ this._token,
1182
+ "/v1/storage/exists",
1183
+ { key },
1184
+ "storage"
1185
+ );
1186
+ return result.exists;
1187
+ } catch (err) {
1188
+ if (err instanceof BrokrError) err.component = "Storage";
1189
+ return false;
1190
+ }
1191
+ }
1192
+ /**
1193
+ * Get file metadata without downloading the file.
1194
+ */
1195
+ async metadata(key) {
1196
+ requireToken(this._token, "storage");
1197
+ try {
1198
+ return await gatewayFetch(
1199
+ this._gatewayUrl,
1200
+ this._token,
1201
+ "/v1/storage/metadata",
1202
+ { key },
1203
+ "storage"
1204
+ );
1205
+ } catch (err) {
1206
+ if (err instanceof BrokrError) err.component = "Storage";
1207
+ throw err;
1208
+ }
1209
+ }
1210
+ // ---------------------------------------------------------------------------
1211
+ // Deprecated aliases (backward compat)
1212
+ // ---------------------------------------------------------------------------
1213
+ /** @deprecated Use signedUrl() instead. */
1214
+ async url(key, options) {
1215
+ return this.signedUrl(key, options);
1216
+ }
1217
+ /** @deprecated Use signedUrl() instead. */
1218
+ async getUrl(key, options) {
1219
+ return this.signedUrl(key, options);
1220
+ }
1221
+ /** @deprecated Use signUpload() instead. */
1222
+ async getUploadUrl(filename, contentType) {
1223
+ return this.signUpload({ fileName: filename, contentType });
1224
+ }
1225
+ };
1226
+ var MAX_PARTS_PER_UPLOAD = 1e4;
1227
+ async function toUint8Array(data) {
1228
+ if (data instanceof Uint8Array) return data;
1229
+ if (typeof data === "string") return new TextEncoder().encode(data);
1230
+ if (typeof Blob !== "undefined" && data instanceof Blob) {
1231
+ return new Uint8Array(await data.arrayBuffer());
1232
+ }
1233
+ throw new Error("Unsupported data type for multipart upload");
1234
+ }
1235
+ function getUploadSize(data) {
1236
+ if (typeof data === "string") {
1237
+ return new TextEncoder().encode(data).length;
1238
+ }
1239
+ if (data instanceof Uint8Array) {
1240
+ return data.byteLength;
1241
+ }
1242
+ if (typeof Blob !== "undefined" && data instanceof Blob) {
1243
+ return data.size;
1244
+ }
1245
+ return void 0;
1246
+ }
1247
+
1248
+ // src/email/templates.ts
1249
+ function interpolate(template, vars) {
1250
+ return template.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? "");
1251
+ }
1252
+ function wrapHtml(body) {
1253
+ return `<!DOCTYPE html>
1254
+ <html>
1255
+ <head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"></head>
1256
+ <body style="margin:0;padding:0;background:#f5f5f5;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
1257
+ <div style="max-width:560px;margin:40px auto;background:#ffffff;border-radius:8px;border:1px solid #e5e5e5;overflow:hidden;">
1258
+ ${body}
1259
+ </div>
1260
+ <div style="text-align:center;padding:20px;color:#999;font-size:12px;">
1261
+ Sent via Brokr
1262
+ </div>
1263
+ </body>
1264
+ </html>`;
1265
+ }
1266
+ function section(content) {
1267
+ return `<div style="padding:32px 40px;">${content}</div>`;
1268
+ }
1269
+ function button(text, urlVar) {
1270
+ return `<a href="{{${urlVar}}}" style="display:inline-block;padding:12px 24px;background:#000;color:#fff;text-decoration:none;border-radius:6px;font-weight:600;font-size:14px;">${text}</a>`;
1271
+ }
1272
+ var welcomeTemplate = {
1273
+ name: "welcome",
1274
+ subject: "Welcome to {{appName}}",
1275
+ html: (vars) => interpolate(wrapHtml(section(`
1276
+ <h1 style="margin:0 0 16px;font-size:24px;color:#111;">Welcome to {{appName}}</h1>
1277
+ <p style="margin:0 0 24px;color:#555;font-size:15px;line-height:1.6;">
1278
+ Your account is ready. You can start using {{appName}} right away.
1279
+ </p>
1280
+ ${button("Get Started", "actionUrl")}
1281
+ `)), vars)
1282
+ };
1283
+ var magicLinkTemplate = {
1284
+ name: "magicLink",
1285
+ subject: "Your sign-in link",
1286
+ html: (vars) => interpolate(wrapHtml(section(`
1287
+ <h1 style="margin:0 0 16px;font-size:24px;color:#111;">Sign in to {{appName}}</h1>
1288
+ <p style="margin:0 0 24px;color:#555;font-size:15px;line-height:1.6;">
1289
+ Click the button below to sign in. This link expires in 10 minutes.
1290
+ </p>
1291
+ ${button("Sign In", "magicLinkUrl")}
1292
+ <p style="margin:24px 0 0;color:#999;font-size:13px;">
1293
+ If you didn't request this, you can safely ignore this email.
1294
+ </p>
1295
+ `)), vars)
1296
+ };
1297
+ var passwordResetTemplate = {
1298
+ name: "passwordReset",
1299
+ subject: "Reset your password",
1300
+ html: (vars) => interpolate(wrapHtml(section(`
1301
+ <h1 style="margin:0 0 16px;font-size:24px;color:#111;">Reset your password</h1>
1302
+ <p style="margin:0 0 24px;color:#555;font-size:15px;line-height:1.6;">
1303
+ We received a request to reset your password for {{appName}}.
1304
+ Click the button below to choose a new password.
1305
+ </p>
1306
+ ${button("Reset Password", "resetUrl")}
1307
+ <p style="margin:24px 0 0;color:#999;font-size:13px;">
1308
+ This link expires in 1 hour. If you didn't request this, ignore this email.
1309
+ </p>
1310
+ `)), vars)
1311
+ };
1312
+ var invoiceTemplate = {
1313
+ name: "invoice",
1314
+ subject: "Invoice #{{invoiceNumber}}",
1315
+ html: (vars) => interpolate(wrapHtml(section(`
1316
+ <h1 style="margin:0 0 16px;font-size:24px;color:#111;">Invoice #{{invoiceNumber}}</h1>
1317
+ <p style="margin:0 0 8px;color:#555;font-size:15px;line-height:1.6;">
1318
+ Amount: <strong>{{amount}}</strong>
1319
+ </p>
1320
+ <p style="margin:0 0 24px;color:#555;font-size:15px;line-height:1.6;">
1321
+ {{description}}
1322
+ </p>
1323
+ ${button("View Invoice", "invoiceUrl")}
1324
+ `)), vars)
1325
+ };
1326
+ var notificationTemplate = {
1327
+ name: "notification",
1328
+ subject: "{{title}}",
1329
+ html: (vars) => interpolate(wrapHtml(section(`
1330
+ <h1 style="margin:0 0 16px;font-size:24px;color:#111;">{{title}}</h1>
1331
+ <p style="margin:0 0 24px;color:#555;font-size:15px;line-height:1.6;">
1332
+ {{body}}
1333
+ </p>
1334
+ `)), vars)
1335
+ };
1336
+ var builtinTemplates = {
1337
+ welcome: welcomeTemplate,
1338
+ magicLink: magicLinkTemplate,
1339
+ passwordReset: passwordResetTemplate,
1340
+ invoice: invoiceTemplate,
1341
+ notification: notificationTemplate
1342
+ };
1343
+
1344
+ // src/email/client.ts
1345
+ function interpolate2(template, vars) {
1346
+ return template.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? "");
1347
+ }
1348
+ var BrokrEmailClient = class {
1349
+ constructor(_token, _gatewayUrl) {
1350
+ this._token = _token;
1351
+ this._gatewayUrl = _gatewayUrl;
1352
+ }
1353
+ /**
1354
+ * Send an email. The from address and API credentials are resolved server-side.
1355
+ */
1356
+ async send(params) {
1357
+ requireToken(this._token, "email");
1358
+ try {
1359
+ return await gatewayFetch(
1360
+ this._gatewayUrl,
1361
+ this._token,
1362
+ "/v1/email/send",
1363
+ params,
1364
+ "email"
1365
+ );
1366
+ } catch (err) {
1367
+ if (err instanceof BrokrError) err.component = "Email";
1368
+ throw err;
1369
+ }
1370
+ }
1371
+ /**
1372
+ * Send an email using a built-in template.
1373
+ *
1374
+ * @example
1375
+ * ```typescript
1376
+ * await brokr.email.sendTemplate({
1377
+ * template: 'welcome',
1378
+ * to: 'user@example.com',
1379
+ * variables: { appName: 'MyApp', actionUrl: 'https://myapp.com/dashboard' },
1380
+ * });
1381
+ * ```
1382
+ */
1383
+ async sendTemplate(params) {
1384
+ try {
1385
+ const template = builtinTemplates[params.template];
1386
+ if (!template) {
1387
+ throw new BrokrError(
1388
+ `[brokr] Unknown email template "${params.template}". Available: ${Object.keys(builtinTemplates).join(", ")}`,
1389
+ "EMAIL_TEMPLATE_NOT_FOUND",
465
1390
  "email"
466
1391
  );
467
1392
  }
1393
+ const subject = interpolate2(template.subject, params.variables);
1394
+ const html = template.html(params.variables);
1395
+ return await this.send({
1396
+ to: params.to,
1397
+ subject,
1398
+ html,
1399
+ from: params.from
1400
+ });
1401
+ } catch (err) {
1402
+ if (err instanceof BrokrError) err.component = "Email";
1403
+ throw err;
1404
+ }
1405
+ }
1406
+ };
1407
+
1408
+ // src/files/client.ts
1409
+ var BrokrFilesClient = class {
1410
+ constructor(_token, _gatewayUrl, _storage) {
1411
+ this._token = _token;
1412
+ this._gatewayUrl = _gatewayUrl;
1413
+ this._storage = _storage;
1414
+ }
1415
+ /**
1416
+ * Upload a file and trigger server-side AI processing.
1417
+ * Returns the storage key plus any extracted description/text.
1418
+ */
1419
+ async process(params) {
1420
+ requireToken(this._token, "files");
1421
+ const uploaded = await this._storage.upload({
1422
+ file: params.file,
1423
+ path: params.fileName,
1424
+ contentType: params.purpose === "text-extraction" ? "application/pdf" : void 0
1425
+ });
1426
+ const result = await gatewayFetch(
1427
+ this._gatewayUrl,
1428
+ this._token,
1429
+ "/v1/files/process",
1430
+ { key: uploaded.key, purpose: params.purpose ?? "general" },
1431
+ "files"
1432
+ );
1433
+ return {
1434
+ key: uploaded.key,
1435
+ description: result.description,
1436
+ text: result.text
468
1437
  };
469
- BrokrRuntime = class {
470
- constructor(options) {
471
- this._token = options?.token ?? resolveToken();
472
- this._gatewayUrl = options?.gatewayUrl ?? GATEWAY_URL;
473
- this.ai = new BrokrAIClient(this._token, this._gatewayUrl);
474
- this.storage = new BrokrStorageClient(this._token, this._gatewayUrl);
475
- this.email = new BrokrEmailClient(this._token, this._gatewayUrl);
476
- }
477
- /** Auth client — lazily initialized to avoid pulling in auth deps when not needed. */
478
- get auth() {
479
- if (!this._auth) {
480
- const { BrokrAuthClient: BrokrAuthClient2 } = (init_auth(), __toCommonJS(auth_exports));
481
- this._auth = new BrokrAuthClient2(this._token, this._gatewayUrl);
482
- }
483
- return this._auth;
484
- }
1438
+ }
1439
+ /**
1440
+ * Get an AI-generated description of an already-uploaded file.
1441
+ */
1442
+ async describe(key) {
1443
+ requireToken(this._token, "files");
1444
+ return gatewayFetch(
1445
+ this._gatewayUrl,
1446
+ this._token,
1447
+ "/v1/files/describe",
1448
+ { key },
1449
+ "files"
1450
+ );
1451
+ }
1452
+ /**
1453
+ * Extract text from a PDF or image (OCR).
1454
+ */
1455
+ async getText(key) {
1456
+ requireToken(this._token, "files");
1457
+ return gatewayFetch(
1458
+ this._gatewayUrl,
1459
+ this._token,
1460
+ "/v1/files/text",
1461
+ { key },
1462
+ "files"
1463
+ );
1464
+ }
1465
+ };
1466
+
1467
+ // src/payments/client.ts
1468
+ var BrokrPaymentsClient = class {
1469
+ constructor(_token, _gatewayUrl) {
1470
+ this._token = _token;
1471
+ this._gatewayUrl = _gatewayUrl;
1472
+ }
1473
+ /**
1474
+ * Create a Stripe Checkout session for a plan.
1475
+ * Returns a URL to redirect the end user to.
1476
+ *
1477
+ * @example
1478
+ * ```ts
1479
+ * const user = await brokr.auth.requireUser(request.headers);
1480
+ * const { checkoutUrl } = await brokr.payments.checkout({
1481
+ * plan: 'pro',
1482
+ * userId: user.id,
1483
+ * });
1484
+ * // Redirect user to checkoutUrl
1485
+ * ```
1486
+ */
1487
+ /**
1488
+ * Create a Stripe Checkout session for a plan.
1489
+ *
1490
+ * @param params.plan - Plan slug (e.g. 'pro')
1491
+ * @param params.userId - End user's ID from your auth system
1492
+ * @param params.returnUrl - Where to redirect after checkout. Defaults to your stack's URL.
1493
+ * Pass explicitly for localhost or non-Brokr deployments.
1494
+ */
1495
+ async checkout(params) {
1496
+ requireToken(this._token, "payments");
1497
+ try {
1498
+ return await gatewayFetch(
1499
+ this._gatewayUrl,
1500
+ this._token,
1501
+ "/v1/payments/checkout",
1502
+ { planSlug: params.plan, appUserId: params.userId, returnUrl: params.returnUrl },
1503
+ "payments"
1504
+ );
1505
+ } catch (err) {
1506
+ if (err instanceof BrokrError) err.component = "Payments";
1507
+ throw err;
1508
+ }
1509
+ }
1510
+ /**
1511
+ * Create a Stripe Customer Portal session.
1512
+ * Returns a URL where the end user can manage their subscription.
1513
+ */
1514
+ /**
1515
+ * Create a Stripe Customer Portal session.
1516
+ *
1517
+ * @param params.userId - End user's ID
1518
+ * @param params.returnUrl - Where to redirect when they're done. Defaults to your stack's URL.
1519
+ */
1520
+ async portal(params) {
1521
+ requireToken(this._token, "payments");
1522
+ try {
1523
+ return await gatewayFetch(
1524
+ this._gatewayUrl,
1525
+ this._token,
1526
+ "/v1/payments/portal",
1527
+ { appUserId: params.userId, returnUrl: params.returnUrl },
1528
+ "payments"
1529
+ );
1530
+ } catch (err) {
1531
+ if (err instanceof BrokrError) err.component = "Payments";
1532
+ throw err;
1533
+ }
1534
+ }
1535
+ /**
1536
+ * Get the end user's current plan.
1537
+ * Returns null if the user has no subscription.
1538
+ */
1539
+ async currentPlan(params) {
1540
+ requireToken(this._token, "payments");
1541
+ try {
1542
+ return await gatewayFetch(
1543
+ this._gatewayUrl,
1544
+ this._token,
1545
+ "/v1/payments/plan",
1546
+ { appUserId: params.userId },
1547
+ "payments"
1548
+ );
1549
+ } catch (err) {
1550
+ if (err instanceof BrokrError) err.component = "Payments";
1551
+ throw err;
1552
+ }
1553
+ }
1554
+ };
1555
+
1556
+ // src/payments/entitlements.ts
1557
+ var BrokrEntitlementsClient = class {
1558
+ constructor(_token, _gatewayUrl) {
1559
+ this._token = _token;
1560
+ this._gatewayUrl = _gatewayUrl;
1561
+ }
1562
+ /**
1563
+ * Check if an end user is entitled to a feature.
1564
+ * Returns true/false without throwing.
1565
+ *
1566
+ * @example
1567
+ * ```ts
1568
+ * const user = await brokr.auth.requireUser(request.headers);
1569
+ * const canChat = await brokr.entitlements.check({ feature: 'ai.chat', userId: user.id });
1570
+ * ```
1571
+ */
1572
+ async check(params) {
1573
+ requireToken(this._token, "payments");
1574
+ const result = await gatewayFetch(
1575
+ this._gatewayUrl,
1576
+ this._token,
1577
+ "/v1/payments/entitlements/check",
1578
+ { feature: params.feature, appUserId: params.userId },
1579
+ "payments"
1580
+ );
1581
+ return result.allowed;
1582
+ }
1583
+ /**
1584
+ * Require that an end user is entitled to a feature.
1585
+ * Throws BrokrError if not entitled.
1586
+ */
1587
+ async require(params) {
1588
+ const allowed = await this.check(params);
1589
+ if (!allowed) {
1590
+ throw new BrokrError(
1591
+ `[brokr] Feature "${params.feature}" is not available on the user's current plan. Upgrade at the billing portal.`,
1592
+ "ENTITLEMENT_DENIED",
1593
+ "payments"
1594
+ );
1595
+ }
1596
+ }
1597
+ /**
1598
+ * Get usage stats for a metered feature.
1599
+ * This is a READ — does NOT increment the counter.
1600
+ * Use `increment()` to record usage.
1601
+ */
1602
+ async usage(params) {
1603
+ requireToken(this._token, "payments");
1604
+ return gatewayFetch(
1605
+ this._gatewayUrl,
1606
+ this._token,
1607
+ "/v1/payments/entitlements/usage",
1608
+ { feature: params.feature, appUserId: params.userId },
1609
+ "payments"
1610
+ );
1611
+ }
1612
+ /**
1613
+ * Record a usage event for a metered feature.
1614
+ * This is a WRITE — increments the counter.
1615
+ */
1616
+ async increment(params) {
1617
+ requireToken(this._token, "payments");
1618
+ await gatewayFetch(
1619
+ this._gatewayUrl,
1620
+ this._token,
1621
+ "/v1/payments/entitlements/increment",
1622
+ { feature: params.feature, appUserId: params.userId, amount: params.amount ?? 1 },
1623
+ "payments"
1624
+ );
1625
+ }
1626
+ };
1627
+
1628
+ // src/notifications/client.ts
1629
+ var BrokrNotificationsClient = class {
1630
+ constructor(token, gatewayUrl) {
1631
+ this.token = token;
1632
+ this.gatewayUrl = gatewayUrl;
1633
+ }
1634
+ /**
1635
+ * Send a notification to a user. Delivered via WebSocket in real-time
1636
+ * and persisted in the notification history.
1637
+ */
1638
+ async send(params) {
1639
+ requireToken(this.token, "notifications");
1640
+ try {
1641
+ return await gatewayFetch(
1642
+ this.gatewayUrl,
1643
+ this.token,
1644
+ "/v1/notifications/push",
1645
+ params,
1646
+ "notifications"
1647
+ );
1648
+ } catch (err) {
1649
+ if (err instanceof BrokrError) err.component = "Notifications";
1650
+ throw err;
1651
+ }
1652
+ }
1653
+ /**
1654
+ * Fetch notification history for a user. Reads from the DO's SQLite
1655
+ * storage via a gateway REST endpoint.
1656
+ */
1657
+ async list(params) {
1658
+ requireToken(this.token, "notifications");
1659
+ try {
1660
+ return await gatewayFetch(
1661
+ this.gatewayUrl,
1662
+ this.token,
1663
+ "/v1/notifications/list",
1664
+ params,
1665
+ "notifications"
1666
+ );
1667
+ } catch (err) {
1668
+ if (err instanceof BrokrError) err.component = "Notifications";
1669
+ throw err;
1670
+ }
1671
+ }
1672
+ };
1673
+
1674
+ // src/auth.ts
1675
+ function resolveAppUrl(appUrl) {
1676
+ if (appUrl) return appUrl;
1677
+ throw new BrokrError(
1678
+ "[brokr] BROKR_AUTH_URL is not set. Auth may not be provisioned.",
1679
+ "AUTH_NOT_CONFIGURED",
1680
+ "auth"
1681
+ );
1682
+ }
1683
+ function mapSessionUser(raw) {
1684
+ return {
1685
+ id: raw.id,
1686
+ email: raw.email,
1687
+ name: raw.name ?? null,
1688
+ image: raw.image ?? null,
1689
+ emailVerified: raw.emailVerified ? /* @__PURE__ */ new Date() : null
1690
+ };
1691
+ }
1692
+ function pluralizeResource(resource) {
1693
+ if (resource.endsWith("s")) return resource;
1694
+ if (resource.endsWith("y") && !/[aeiou]y$/i.test(resource)) return resource.slice(0, -1) + "ies";
1695
+ return resource + "s";
1696
+ }
1697
+ function validateSqlIdentifier(name, label) {
1698
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
1699
+ throw new BrokrError(
1700
+ `[brokr] Invalid ${label}: "${name}". Must be alphanumeric with underscores only.`,
1701
+ "INVALID_IDENTIFIER",
1702
+ "auth"
1703
+ );
1704
+ }
1705
+ }
1706
+ var _ownershipPool = null;
1707
+ async function getOwnershipPool(dbUrl) {
1708
+ let Pool;
1709
+ try {
1710
+ const mod = await import("@neondatabase/serverless");
1711
+ Pool = mod.Pool;
1712
+ } catch {
1713
+ throw new BrokrError(
1714
+ "[brokr] @neondatabase/serverless is required for ownership checks. Run: npm install @neondatabase/serverless",
1715
+ "MISSING_DEPENDENCY",
1716
+ "auth"
1717
+ );
1718
+ }
1719
+ if (_ownershipPool && _ownershipPool.dbUrl === dbUrl) {
1720
+ return _ownershipPool.pool;
1721
+ }
1722
+ const pool = new Pool({ connectionString: dbUrl, max: 3 });
1723
+ _ownershipPool = { pool, dbUrl };
1724
+ return pool;
1725
+ }
1726
+ async function authApiFetch(appUrl, path, options) {
1727
+ const headers = { "Content-Type": "application/json" };
1728
+ if (options?.cookies) headers.cookie = options.cookies;
1729
+ if (typeof window === "undefined") {
1730
+ 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;
1731
+ }
1732
+ const res = await fetch(`${appUrl}${path}`, {
1733
+ method: options?.method ?? "GET",
1734
+ headers,
1735
+ body: options?.body ? JSON.stringify(options.body) : void 0
1736
+ });
1737
+ if (!res.ok) {
1738
+ const data = await res.json().catch(() => ({}));
1739
+ throw new BrokrError(
1740
+ data.message ?? `[brokr] Auth API call failed (HTTP ${res.status})`,
1741
+ "AUTH_API_FAILED",
1742
+ "auth"
1743
+ );
1744
+ }
1745
+ return res.json();
1746
+ }
1747
+ var BrokrAuthClient = class {
1748
+ constructor(_token, _gatewayUrl, appUrl) {
1749
+ this._appUrl = appUrl ?? (typeof process !== "undefined" ? process.env.BROKR_AUTH_URL : void 0);
1750
+ }
1751
+ // -------------------------------------------------------------------------
1752
+ // Identity — read user/session from incoming request
1753
+ // -------------------------------------------------------------------------
1754
+ /**
1755
+ * Get user from request headers. Returns null if not authenticated.
1756
+ * Calls the app's own Better Auth API.
1757
+ */
1758
+ async user(headers) {
1759
+ const session = await this.session(headers);
1760
+ return session?.user ?? null;
1761
+ }
1762
+ /**
1763
+ * Get user from request headers. Throws 401 if not authenticated.
1764
+ */
1765
+ async requireUser(headers) {
1766
+ const u = await this.user(headers);
1767
+ if (!u) {
1768
+ throw new BrokrError("[brokr] Authentication required. The request has no valid session. Check that cookies are being forwarded.", "UNAUTHORIZED", "auth");
1769
+ }
1770
+ return u;
1771
+ }
1772
+ /**
1773
+ * Get full session from request headers. Returns null if not authenticated.
1774
+ */
1775
+ async session(headers) {
1776
+ const appUrl = resolveAppUrl(this._appUrl);
1777
+ const cookieHeader = headers.get("cookie") ?? "";
1778
+ if (!cookieHeader) return null;
1779
+ const res = await fetch(`${appUrl}/api/auth/get-session`, {
1780
+ method: "GET",
1781
+ headers: { cookie: cookieHeader }
1782
+ });
1783
+ if (!res.ok) return null;
1784
+ const data = await res.json();
1785
+ if (!data?.user || !data?.session) return null;
1786
+ return {
1787
+ user: mapSessionUser(data.user),
1788
+ sessionId: data.session.id,
1789
+ expiresAt: new Date(data.session.expiresAt)
485
1790
  };
486
1791
  }
487
- });
488
- init_runtime();
1792
+ /**
1793
+ * Get full session. Throws 401 if not authenticated.
1794
+ */
1795
+ async requireSession(headers) {
1796
+ const s = await this.session(headers);
1797
+ if (!s) {
1798
+ throw new BrokrError("[brokr] Authentication required. The request has no valid session. Check that cookies are being forwarded.", "UNAUTHORIZED", "auth");
1799
+ }
1800
+ return s;
1801
+ }
1802
+ // -------------------------------------------------------------------------
1803
+ // Legacy aliases (backward compat)
1804
+ // -------------------------------------------------------------------------
1805
+ /** @deprecated Use user(request.headers) instead. */
1806
+ async currentUser(request) {
1807
+ return this.user(request.headers);
1808
+ }
1809
+ /** @deprecated Use session(request.headers) instead. */
1810
+ async getSession(request) {
1811
+ return this.session(request.headers);
1812
+ }
1813
+ // -------------------------------------------------------------------------
1814
+ // Auth actions — call Better Auth endpoints on the stack's app
1815
+ // -------------------------------------------------------------------------
1816
+ /**
1817
+ * Sign in with email and password.
1818
+ */
1819
+ async signIn(params) {
1820
+ const appUrl = resolveAppUrl(this._appUrl);
1821
+ const data = await authApiFetch(appUrl, "/api/auth/sign-in/email", {
1822
+ method: "POST",
1823
+ body: params
1824
+ });
1825
+ return {
1826
+ user: mapSessionUser(data.user),
1827
+ sessionId: data.session.id,
1828
+ expiresAt: new Date(data.session.expiresAt)
1829
+ };
1830
+ }
1831
+ /**
1832
+ * Sign in with OAuth provider. Returns redirect URL.
1833
+ */
1834
+ async signInWithProvider(provider, options) {
1835
+ const appUrl = resolveAppUrl(this._appUrl);
1836
+ const redirectTo = options?.redirectTo ?? "/";
1837
+ if (!redirectTo.startsWith("/") || redirectTo.startsWith("//")) {
1838
+ throw new BrokrError("[brokr] redirectTo must be a relative path (start with /)", "INVALID_REDIRECT", "auth");
1839
+ }
1840
+ if (!/^[a-z][a-z0-9_-]*$/.test(provider)) {
1841
+ throw new BrokrError(`[brokr] Invalid provider name: "${provider}". Must be lowercase alphanumeric.`, "INVALID_PROVIDER", "auth");
1842
+ }
1843
+ const params = new URLSearchParams({ provider, callbackURL: redirectTo });
1844
+ return { redirectUrl: `${appUrl}/api/auth/sign-in/social?${params}` };
1845
+ }
1846
+ /**
1847
+ * Sign up with email and password.
1848
+ */
1849
+ async signUp(params) {
1850
+ const appUrl = resolveAppUrl(this._appUrl);
1851
+ const data = await authApiFetch(appUrl, "/api/auth/sign-up/email", {
1852
+ method: "POST",
1853
+ body: params
1854
+ });
1855
+ return {
1856
+ user: mapSessionUser(data.user),
1857
+ sessionId: data.session.id,
1858
+ expiresAt: new Date(data.session.expiresAt)
1859
+ };
1860
+ }
1861
+ /**
1862
+ * Sign out the current user.
1863
+ */
1864
+ async signOut(headers) {
1865
+ const appUrl = resolveAppUrl(this._appUrl);
1866
+ const cookies = headers?.get("cookie") ?? "";
1867
+ await authApiFetch(appUrl, "/api/auth/sign-out", {
1868
+ method: "POST",
1869
+ cookies
1870
+ });
1871
+ }
1872
+ /**
1873
+ * Send a magic link email.
1874
+ */
1875
+ async sendMagicLink(email, options) {
1876
+ const appUrl = resolveAppUrl(this._appUrl);
1877
+ await authApiFetch(appUrl, "/api/auth/magic-link/send", {
1878
+ method: "POST",
1879
+ body: { email, callbackURL: options?.redirectTo ?? "/" }
1880
+ });
1881
+ }
1882
+ /**
1883
+ * Send a password reset email.
1884
+ */
1885
+ async sendPasswordReset(params) {
1886
+ const appUrl = resolveAppUrl(this._appUrl);
1887
+ await authApiFetch(appUrl, "/api/auth/forget-password", {
1888
+ method: "POST",
1889
+ body: params
1890
+ });
1891
+ }
1892
+ /**
1893
+ * Reset password with token from email.
1894
+ */
1895
+ async resetPassword(params) {
1896
+ const appUrl = resolveAppUrl(this._appUrl);
1897
+ await authApiFetch(appUrl, "/api/auth/reset-password", {
1898
+ method: "POST",
1899
+ body: { token: params.token, newPassword: params.password }
1900
+ });
1901
+ }
1902
+ /**
1903
+ * Verify email with token from email.
1904
+ */
1905
+ async verifyEmail(params) {
1906
+ const appUrl = resolveAppUrl(this._appUrl);
1907
+ await authApiFetch(appUrl, "/api/auth/verify-email", {
1908
+ method: "POST",
1909
+ body: params
1910
+ });
1911
+ }
1912
+ /**
1913
+ * Update the current user's profile.
1914
+ */
1915
+ async updateUser(params, headers) {
1916
+ const appUrl = resolveAppUrl(this._appUrl);
1917
+ const cookies = headers?.get("cookie") ?? "";
1918
+ await authApiFetch(appUrl, "/api/auth/update-user", {
1919
+ method: "POST",
1920
+ body: params,
1921
+ cookies
1922
+ });
1923
+ }
1924
+ // -------------------------------------------------------------------------
1925
+ // Session management
1926
+ // -------------------------------------------------------------------------
1927
+ /** Session management sub-client. */
1928
+ get sessions() {
1929
+ if (!this._sessions) {
1930
+ this._sessions = new BrokrSessionsClient(this._appUrl);
1931
+ }
1932
+ return this._sessions;
1933
+ }
1934
+ // -------------------------------------------------------------------------
1935
+ // Ownership — queries the stack's own DB via gateway → server
1936
+ // -------------------------------------------------------------------------
1937
+ /**
1938
+ * Check if the current user owns a resource. Throws 403 if not.
1939
+ *
1940
+ * Queries the developer's own database directly via DATABASE_URL.
1941
+ * Convention: pluralizes resource name → table, assumes `id` PK + `user_id` owner column.
1942
+ * Override with `opts.table` and `opts.ownerColumn` for non-standard schemas.
1943
+ *
1944
+ * @example
1945
+ * ```ts
1946
+ * await brokr.auth.requireOwner('conversation', conversationId, request.headers);
1947
+ * // Under the hood: SELECT 1 FROM conversations WHERE id = $1 AND user_id = $2
1948
+ * ```
1949
+ */
1950
+ async requireOwner(resource, id, headers, opts) {
1951
+ const isOwner = await this.isOwner(resource, id, headers, opts);
1952
+ if (!isOwner) {
1953
+ throw new BrokrError(
1954
+ `[brokr] Access denied \u2014 current user does not own this ${resource}. Verify the resource ID and that the user is the owner.`,
1955
+ "FORBIDDEN",
1956
+ "auth"
1957
+ );
1958
+ }
1959
+ }
1960
+ /**
1961
+ * Check if the current user owns a resource. Returns boolean.
1962
+ *
1963
+ * Runs a direct query against the developer's database (DATABASE_URL).
1964
+ * No gateway round-trip — this is a local DB operation.
1965
+ */
1966
+ async isOwner(resource, id, headers, opts) {
1967
+ const user = await this.requireUser(headers);
1968
+ const dbUrl = typeof process !== "undefined" ? process.env.DATABASE_URL : void 0;
1969
+ if (!dbUrl) {
1970
+ throw new BrokrError(
1971
+ "[brokr] DATABASE_URL is not set. Cannot check ownership without a database.",
1972
+ "DB_NOT_CONFIGURED",
1973
+ "auth"
1974
+ );
1975
+ }
1976
+ const tableName = opts?.table ?? pluralizeResource(resource);
1977
+ const ownerCol = opts?.ownerColumn ?? "user_id";
1978
+ validateSqlIdentifier(tableName, "table name");
1979
+ validateSqlIdentifier(ownerCol, "owner column");
1980
+ const pool = await getOwnershipPool(dbUrl);
1981
+ try {
1982
+ const result = await pool.query(
1983
+ `SELECT 1 FROM "${tableName}" WHERE id = $1 AND "${ownerCol}" = $2 LIMIT 1`,
1984
+ [id, user.id]
1985
+ );
1986
+ return (result.rowCount ?? 0) > 0;
1987
+ } catch (err) {
1988
+ const msg = err instanceof Error ? err.message : String(err);
1989
+ console.error("[brokr] Ownership check error:", msg);
1990
+ if (msg.includes("does not exist")) {
1991
+ throw new BrokrError(
1992
+ `[brokr] Table "${tableName}" does not exist. Check your resource name or provide { table: '...' }.`,
1993
+ "TABLE_NOT_FOUND",
1994
+ "auth"
1995
+ );
1996
+ }
1997
+ throw new BrokrError(
1998
+ "[brokr] Ownership check failed. Check your DATABASE_URL and that the database is accessible.",
1999
+ "DB_QUERY_FAILED",
2000
+ "auth"
2001
+ );
2002
+ }
2003
+ }
2004
+ /**
2005
+ * Check if the current user matches a specific userId. Throws 403 if not.
2006
+ */
2007
+ async requireCurrentUser(userId, headers) {
2008
+ const user = await this.requireUser(headers);
2009
+ if (user.id !== userId) {
2010
+ throw new BrokrError("[brokr] Access denied.", "FORBIDDEN", "auth");
2011
+ }
2012
+ }
2013
+ // -------------------------------------------------------------------------
2014
+ // Legacy alias
2015
+ // -------------------------------------------------------------------------
2016
+ /** @deprecated Use signInWithProvider() instead. */
2017
+ async getOAuthUrl(provider, options) {
2018
+ return this.signInWithProvider(provider, options);
2019
+ }
2020
+ };
2021
+ var BrokrSessionsClient = class {
2022
+ constructor(appUrl) {
2023
+ this._appUrl = appUrl;
2024
+ }
2025
+ /**
2026
+ * List all active sessions for the current user.
2027
+ */
2028
+ async list(headers) {
2029
+ const appUrl = resolveAppUrl(this._appUrl);
2030
+ const cookies = headers.get("cookie") ?? "";
2031
+ const data = await authApiFetch(appUrl, "/api/auth/list-sessions", { cookies });
2032
+ const sessionToken = parseCookieValue(cookies, "better-auth.session_token") ?? parseCookieValue(cookies, "__Secure-better-auth.session_token");
2033
+ return data.map((s) => ({
2034
+ id: s.id,
2035
+ createdAt: new Date(s.createdAt),
2036
+ expiresAt: new Date(s.expiresAt),
2037
+ userAgent: s.userAgent,
2038
+ ipAddress: s.ipAddress,
2039
+ isCurrent: s.token === sessionToken
2040
+ }));
2041
+ }
2042
+ /**
2043
+ * Revoke a specific session by ID.
2044
+ */
2045
+ async revoke(sessionId, headers) {
2046
+ const appUrl = resolveAppUrl(this._appUrl);
2047
+ const cookies = headers.get("cookie") ?? "";
2048
+ await authApiFetch(appUrl, "/api/auth/revoke-session", {
2049
+ method: "POST",
2050
+ body: { id: sessionId },
2051
+ cookies
2052
+ });
2053
+ }
2054
+ /**
2055
+ * Revoke all sessions except the current one.
2056
+ */
2057
+ async revokeOthers(headers) {
2058
+ const appUrl = resolveAppUrl(this._appUrl);
2059
+ const cookies = headers.get("cookie") ?? "";
2060
+ await authApiFetch(appUrl, "/api/auth/revoke-other-sessions", {
2061
+ method: "POST",
2062
+ cookies
2063
+ });
2064
+ }
2065
+ };
2066
+ function parseCookies(cookieHeader) {
2067
+ const cookies = /* @__PURE__ */ new Map();
2068
+ for (const pair of cookieHeader.split(";")) {
2069
+ const eqIdx = pair.indexOf("=");
2070
+ if (eqIdx === -1) continue;
2071
+ const name = pair.slice(0, eqIdx).trim();
2072
+ const value = pair.slice(eqIdx + 1).trim();
2073
+ if (name) cookies.set(name, value);
2074
+ }
2075
+ return cookies;
2076
+ }
2077
+ function parseCookieValue(cookieHeader, name) {
2078
+ return parseCookies(cookieHeader).get(name);
2079
+ }
2080
+
2081
+ // src/models.ts
2082
+ var models = {
2083
+ /** Cheapest and fastest model (Deepseek Chat). */
2084
+ FAST: "deepseek-chat",
2085
+ /** Most capable model. */
2086
+ SMART: "claude-sonnet-4-6",
2087
+ /** Default balanced model (Deepseek Chat). */
2088
+ BALANCED: "deepseek-chat"
2089
+ };
2090
+
2091
+ // src/runtime.ts
2092
+ var BrokrRuntime = class {
2093
+ constructor(options) {
2094
+ this._token = options?.token ?? resolveToken();
2095
+ this._gatewayUrl = options?.gatewayUrl ?? GATEWAY_URL;
2096
+ this.ai = new BrokrAIClient(this._token, this._gatewayUrl);
2097
+ this.storage = new BrokrStorageClient(this._token, this._gatewayUrl);
2098
+ this.email = new BrokrEmailClient(this._token, this._gatewayUrl);
2099
+ }
2100
+ // -------------------------------------------------------------------------
2101
+ // Lazy namespace getters
2102
+ // -------------------------------------------------------------------------
2103
+ /** Files client (upload + AI processing). */
2104
+ get files() {
2105
+ if (!this._files) {
2106
+ this._files = new BrokrFilesClient(this._token, this._gatewayUrl, this.storage);
2107
+ }
2108
+ return this._files;
2109
+ }
2110
+ /** Payments client (checkout, portal, plan queries). */
2111
+ get payments() {
2112
+ if (!this._payments) {
2113
+ this._payments = new BrokrPaymentsClient(this._token, this._gatewayUrl);
2114
+ }
2115
+ return this._payments;
2116
+ }
2117
+ /** Notifications client (send + list notifications). */
2118
+ get notifications() {
2119
+ if (!this._notifications) {
2120
+ this._notifications = new BrokrNotificationsClient(this._token, this._gatewayUrl);
2121
+ }
2122
+ return this._notifications;
2123
+ }
2124
+ /** Entitlements client (feature checks, usage queries). */
2125
+ get entitlements() {
2126
+ if (!this._entitlements) {
2127
+ this._entitlements = new BrokrEntitlementsClient(this._token, this._gatewayUrl);
2128
+ }
2129
+ return this._entitlements;
2130
+ }
2131
+ /** Auth client — lazily initialized. */
2132
+ get auth() {
2133
+ if (!this._auth) {
2134
+ this._auth = new BrokrAuthClient(this._token, this._gatewayUrl);
2135
+ }
2136
+ return this._auth;
2137
+ }
2138
+ // -------------------------------------------------------------------------
2139
+ // Root aliases — one-liner DX for the most common operations
2140
+ // -------------------------------------------------------------------------
2141
+ /** Send a chat message. Alias for `brokr.ai.chat()`. */
2142
+ chat(input, options) {
2143
+ return this.ai.chat(input, options);
2144
+ }
2145
+ /** Upload a file. Alias for `brokr.storage.upload()`. */
2146
+ upload(params) {
2147
+ return this.storage.upload(params);
2148
+ }
2149
+ /** Start a checkout flow. Alias for `brokr.payments.checkout()`. */
2150
+ checkout(params) {
2151
+ return this.payments.checkout(params);
2152
+ }
2153
+ /** Start a checkout flow. Alias for `brokr.checkout()` — north star one-liner. */
2154
+ purchase(params) {
2155
+ return this.payments.checkout(params);
2156
+ }
2157
+ };
2158
+ function createBrokr(options) {
2159
+ BrokrDevConsole.install();
2160
+ initCapture({
2161
+ token: options?.token,
2162
+ gatewayUrl: options?.gatewayUrl
2163
+ });
2164
+ return new BrokrRuntime(options);
2165
+ }
489
2166
  export {
490
2167
  BrokrAIClient,
491
2168
  BrokrAuthError,
492
2169
  BrokrEmailClient,
2170
+ BrokrEntitlementsClient,
493
2171
  BrokrError,
2172
+ BrokrFilesClient,
494
2173
  BrokrNetworkError,
2174
+ BrokrNotFoundError,
2175
+ BrokrNotificationsClient,
2176
+ BrokrPaymentsClient,
495
2177
  BrokrRateLimitError,
496
2178
  BrokrRuntime,
497
2179
  BrokrStorageClient,
2180
+ BrokrTimeoutError,
2181
+ BrokrValidationError,
498
2182
  GATEWAY_URL,
499
2183
  createBrokr,
2184
+ detectEnv,
2185
+ isDev,
2186
+ isProd,
2187
+ isStaging,
500
2188
  models
501
2189
  };