@brokr/sdk 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/account.js +34 -0
- package/dist/account.mjs +7 -0
- package/dist/auth.js +628 -114
- package/dist/auth.mjs +611 -111
- package/dist/chat.js +34 -0
- package/dist/chat.mjs +7 -0
- package/dist/events.js +64 -0
- package/dist/events.mjs +37 -0
- package/dist/feature.js +6304 -0
- package/dist/feature.mjs +6278 -0
- package/dist/files.js +428 -0
- package/dist/files.mjs +408 -0
- package/dist/index.d.ts +18 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4069 -454
- package/dist/index.mjs +4040 -448
- package/dist/logs.js +148 -0
- package/dist/logs.mjs +124 -0
- package/dist/management.js +14 -13
- package/dist/management.mjs +14 -13
- package/dist/next.js +2725 -0
- package/dist/next.mjs +2710 -0
- package/dist/notifications.js +140 -0
- package/dist/notifications.mjs +110 -0
- package/dist/payments.js +32 -0
- package/dist/payments.mjs +7 -0
- package/dist/react-notifications.js +286 -0
- package/dist/react-notifications.mjs +254 -0
- package/dist/react-styles.js +2718 -0
- package/dist/react-styles.mjs +2682 -0
- package/dist/react-theme.js +4194 -0
- package/dist/react-theme.mjs +4170 -0
- package/dist/react.js +8512 -209
- package/dist/react.mjs +8488 -179
- package/dist/runtime.js +2113 -385
- package/dist/runtime.mjs +2085 -397
- package/dist/src/account/config.d.ts +42 -0
- package/dist/src/account/config.d.ts.map +1 -0
- package/dist/src/account/index.d.ts +3 -0
- package/dist/src/account/index.d.ts.map +1 -0
- package/dist/src/ai/client.d.ts +58 -0
- package/dist/src/ai/client.d.ts.map +1 -0
- package/dist/src/ai/conversation-title.d.ts +13 -0
- package/dist/src/ai/conversation-title.d.ts.map +1 -0
- package/dist/src/ai/types.d.ts +81 -0
- package/dist/src/ai/types.d.ts.map +1 -0
- package/dist/src/auth.d.ts +133 -20
- package/dist/src/auth.d.ts.map +1 -1
- package/dist/src/chat/config.d.ts +61 -0
- package/dist/src/chat/config.d.ts.map +1 -0
- package/dist/src/chat/index.d.ts +3 -0
- package/dist/src/chat/index.d.ts.map +1 -0
- package/dist/src/chat/sse-parser.d.ts +44 -0
- package/dist/src/chat/sse-parser.d.ts.map +1 -0
- package/dist/src/dev-console.d.ts +18 -0
- package/dist/src/dev-console.d.ts.map +1 -0
- package/dist/src/email/client.d.ts +33 -0
- package/dist/src/email/client.d.ts.map +1 -0
- package/dist/src/email/templates.d.ts +15 -0
- package/dist/src/email/templates.d.ts.map +1 -0
- package/dist/src/email/types.d.ts +35 -0
- package/dist/src/email/types.d.ts.map +1 -0
- package/dist/src/env-detect.d.ts +25 -0
- package/dist/src/env-detect.d.ts.map +1 -0
- package/dist/src/errors.d.ts +53 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/events/client.d.ts +25 -0
- package/dist/src/events/client.d.ts.map +1 -0
- package/dist/src/events/index.d.ts +9 -0
- package/dist/src/events/index.d.ts.map +1 -0
- package/dist/src/events/types.d.ts +10 -0
- package/dist/src/events/types.d.ts.map +1 -0
- package/dist/src/feature/canonical.d.ts +33 -0
- package/dist/src/feature/canonical.d.ts.map +1 -0
- package/dist/src/feature/create-feature.d.ts +33 -0
- package/dist/src/feature/create-feature.d.ts.map +1 -0
- package/dist/src/feature/db.d.ts +16 -0
- package/dist/src/feature/db.d.ts.map +1 -0
- package/dist/src/feature/handlers.d.ts +28 -0
- package/dist/src/feature/handlers.d.ts.map +1 -0
- package/dist/src/feature/index.d.ts +21 -0
- package/dist/src/feature/index.d.ts.map +1 -0
- package/dist/src/feature/manifest.d.ts +173 -0
- package/dist/src/feature/manifest.d.ts.map +1 -0
- package/dist/src/feature/mapping.d.ts +18 -0
- package/dist/src/feature/mapping.d.ts.map +1 -0
- package/dist/src/feature/runtime.d.ts +45 -0
- package/dist/src/feature/runtime.d.ts.map +1 -0
- package/dist/src/feature/types.d.ts +65 -0
- package/dist/src/feature/types.d.ts.map +1 -0
- package/dist/src/files/client.d.ts +28 -0
- package/dist/src/files/client.d.ts.map +1 -0
- package/dist/src/files/types.d.ts +28 -0
- package/dist/src/files/types.d.ts.map +1 -0
- package/dist/src/fix-registry.d.ts +8 -0
- package/dist/src/fix-registry.d.ts.map +1 -0
- package/dist/src/gateway.d.ts +32 -0
- package/dist/src/gateway.d.ts.map +1 -0
- package/dist/src/logs/capture.d.ts +56 -0
- package/dist/src/logs/capture.d.ts.map +1 -0
- package/dist/src/logs/index.d.ts +2 -0
- package/dist/src/logs/index.d.ts.map +1 -0
- package/dist/src/management.d.ts +1 -1
- package/dist/src/management.d.ts.map +1 -1
- package/dist/src/models.d.ts +32 -0
- package/dist/src/models.d.ts.map +1 -0
- package/dist/src/next/auth.d.ts +54 -0
- package/dist/src/next/auth.d.ts.map +1 -0
- package/dist/src/next/chat.d.ts +31 -0
- package/dist/src/next/chat.d.ts.map +1 -0
- package/dist/src/next/index.d.ts +14 -0
- package/dist/src/next/index.d.ts.map +1 -0
- package/dist/src/next/notifications.d.ts +67 -0
- package/dist/src/next/notifications.d.ts.map +1 -0
- package/dist/src/notifications/built-ins.d.ts +9 -0
- package/dist/src/notifications/built-ins.d.ts.map +1 -0
- package/dist/src/notifications/client.d.ts +38 -0
- package/dist/src/notifications/client.d.ts.map +1 -0
- package/dist/src/notifications/config.d.ts +71 -0
- package/dist/src/notifications/config.d.ts.map +1 -0
- package/dist/src/notifications/index.d.ts +6 -0
- package/dist/src/notifications/index.d.ts.map +1 -0
- package/dist/src/notifications/registry.d.ts +67 -0
- package/dist/src/notifications/registry.d.ts.map +1 -0
- package/dist/src/notifications/types.d.ts +48 -0
- package/dist/src/notifications/types.d.ts.map +1 -0
- package/dist/src/payments/client.d.ts +64 -0
- package/dist/src/payments/client.d.ts.map +1 -0
- package/dist/src/payments/config.d.ts +46 -0
- package/dist/src/payments/config.d.ts.map +1 -0
- package/dist/src/payments/entitlements.d.ts +48 -0
- package/dist/src/payments/entitlements.d.ts.map +1 -0
- package/dist/src/payments/types.d.ts +135 -0
- package/dist/src/payments/types.d.ts.map +1 -0
- package/dist/src/react/BrokrErrorBoundary.d.ts +23 -0
- package/dist/src/react/BrokrErrorBoundary.d.ts.map +1 -0
- package/dist/src/react/account/AccountPanel.d.ts +12 -0
- package/dist/src/react/account/AccountPanel.d.ts.map +1 -0
- package/dist/src/react/account/Avatar.d.ts +11 -0
- package/dist/src/react/account/Avatar.d.ts.map +1 -0
- package/dist/src/react/account/ProfilePhotoButton.d.ts +7 -0
- package/dist/src/react/account/ProfilePhotoButton.d.ts.map +1 -0
- package/dist/src/react/account/UserButton.d.ts +7 -0
- package/dist/src/react/account/UserButton.d.ts.map +1 -0
- package/dist/src/react/auth-pages/AuthPageShell.d.ts +9 -0
- package/dist/src/react/auth-pages/AuthPageShell.d.ts.map +1 -0
- package/dist/src/react/auth-pages/SignInPage.d.ts +9 -0
- package/dist/src/react/auth-pages/SignInPage.d.ts.map +1 -0
- package/dist/src/react/auth-pages/SignUpPage.d.ts +8 -0
- package/dist/src/react/auth-pages/SignUpPage.d.ts.map +1 -0
- package/dist/src/react/auth.d.ts +1 -49
- package/dist/src/react/auth.d.ts.map +1 -1
- package/dist/src/react/chat/AIChat.d.ts +4 -0
- package/dist/src/react/chat/AIChat.d.ts.map +1 -0
- package/dist/src/react/chat/ChatContext.d.ts +76 -0
- package/dist/src/react/chat/ChatContext.d.ts.map +1 -0
- package/dist/src/react/chat/ChatInput.d.ts +3 -0
- package/dist/src/react/chat/ChatInput.d.ts.map +1 -0
- package/dist/src/react/chat/MarkdownRenderer.d.ts +5 -0
- package/dist/src/react/chat/MarkdownRenderer.d.ts.map +1 -0
- package/dist/src/react/chat/MessageBubble.d.ts +14 -0
- package/dist/src/react/chat/MessageBubble.d.ts.map +1 -0
- package/dist/src/react/chat/MessagePane.d.ts +10 -0
- package/dist/src/react/chat/MessagePane.d.ts.map +1 -0
- package/dist/src/react/chat/ModelSelector.d.ts +13 -0
- package/dist/src/react/chat/ModelSelector.d.ts.map +1 -0
- package/dist/src/react/chat/ThreadSidebar.d.ts +3 -0
- package/dist/src/react/chat/ThreadSidebar.d.ts.map +1 -0
- package/dist/src/react/chat/index.d.ts +5 -0
- package/dist/src/react/chat/index.d.ts.map +1 -0
- package/dist/src/react/chat/token-limit.d.ts +14 -0
- package/dist/src/react/chat/token-limit.d.ts.map +1 -0
- package/dist/src/react/chat/types.d.ts +65 -0
- package/dist/src/react/chat/types.d.ts.map +1 -0
- package/dist/src/react/chat/useChat.d.ts +57 -0
- package/dist/src/react/chat/useChat.d.ts.map +1 -0
- package/dist/src/react/composites/FabAI.d.ts +15 -0
- package/dist/src/react/composites/FabAI.d.ts.map +1 -0
- package/dist/src/react/composites/FeedbackWidget.d.ts +10 -0
- package/dist/src/react/composites/FeedbackWidget.d.ts.map +1 -0
- package/dist/src/react/composites/SmartUpload.d.ts +12 -0
- package/dist/src/react/composites/SmartUpload.d.ts.map +1 -0
- package/dist/src/react/config.d.ts +23 -0
- package/dist/src/react/config.d.ts.map +1 -0
- package/dist/src/react/context.d.ts +4 -0
- package/dist/src/react/context.d.ts.map +1 -0
- package/dist/src/react/css/account.d.ts +2 -0
- package/dist/src/react/css/account.d.ts.map +1 -0
- package/dist/src/react/css/animations.d.ts +2 -0
- package/dist/src/react/css/animations.d.ts.map +1 -0
- package/dist/src/react/css/auth.d.ts +2 -0
- package/dist/src/react/css/auth.d.ts.map +1 -0
- package/dist/src/react/css/chat-extras.d.ts +2 -0
- package/dist/src/react/css/chat-extras.d.ts.map +1 -0
- package/dist/src/react/css/chat.d.ts +2 -0
- package/dist/src/react/css/chat.d.ts.map +1 -0
- package/dist/src/react/css/composites.d.ts +2 -0
- package/dist/src/react/css/composites.d.ts.map +1 -0
- package/dist/src/react/css/gates.d.ts +2 -0
- package/dist/src/react/css/gates.d.ts.map +1 -0
- package/dist/src/react/css/index.d.ts +3 -0
- package/dist/src/react/css/index.d.ts.map +1 -0
- package/dist/src/react/css/markdown.d.ts +2 -0
- package/dist/src/react/css/markdown.d.ts.map +1 -0
- package/dist/src/react/css/notifications.d.ts +2 -0
- package/dist/src/react/css/notifications.d.ts.map +1 -0
- package/dist/src/react/css/primitives.d.ts +2 -0
- package/dist/src/react/css/primitives.d.ts.map +1 -0
- package/dist/src/react/css/reset.d.ts +2 -0
- package/dist/src/react/css/reset.d.ts.map +1 -0
- package/dist/src/react/css/responsive.d.ts +2 -0
- package/dist/src/react/css/responsive.d.ts.map +1 -0
- package/dist/src/react/css/skeleton.d.ts +2 -0
- package/dist/src/react/css/skeleton.d.ts.map +1 -0
- package/dist/src/react/css/tokens.d.ts +2 -0
- package/dist/src/react/css/tokens.d.ts.map +1 -0
- package/dist/src/react/gates/AuthWall.d.ts +7 -0
- package/dist/src/react/gates/AuthWall.d.ts.map +1 -0
- package/dist/src/react/gates/BillingBoundary.d.ts +4 -0
- package/dist/src/react/gates/BillingBoundary.d.ts.map +1 -0
- package/dist/src/react/gates/Gate.d.ts +9 -0
- package/dist/src/react/gates/Gate.d.ts.map +1 -0
- package/dist/src/react/gates/RequirePlan.d.ts +4 -0
- package/dist/src/react/gates/RequirePlan.d.ts.map +1 -0
- package/dist/src/react/gates/RequireUser.d.ts +4 -0
- package/dist/src/react/gates/RequireUser.d.ts.map +1 -0
- package/dist/src/react/gates/UsageGate.d.ts +4 -0
- package/dist/src/react/gates/UsageGate.d.ts.map +1 -0
- package/dist/src/react/helpers.d.ts +7 -0
- package/dist/src/react/helpers.d.ts.map +1 -0
- package/dist/src/react/hooks/use-theme.d.ts +15 -0
- package/dist/src/react/hooks/use-theme.d.ts.map +1 -0
- package/dist/src/react/hooks/use-user.d.ts +12 -0
- package/dist/src/react/hooks/use-user.d.ts.map +1 -0
- package/dist/src/react/icons.d.ts +26 -0
- package/dist/src/react/icons.d.ts.map +1 -0
- package/dist/src/react/index.d.ts +48 -0
- package/dist/src/react/index.d.ts.map +1 -0
- package/dist/src/react/notifications/NotificationBell.d.ts +7 -0
- package/dist/src/react/notifications/NotificationBell.d.ts.map +1 -0
- package/dist/src/react/notifications/NotificationList.d.ts +7 -0
- package/dist/src/react/notifications/NotificationList.d.ts.map +1 -0
- package/dist/src/react/notifications/Toast.d.ts +13 -0
- package/dist/src/react/notifications/Toast.d.ts.map +1 -0
- package/dist/src/react/notifications/index.d.ts +8 -0
- package/dist/src/react/notifications/index.d.ts.map +1 -0
- package/dist/src/react/notifications/provider.d.ts +14 -0
- package/dist/src/react/notifications/provider.d.ts.map +1 -0
- package/dist/src/react/notifications/use-notifications.d.ts +24 -0
- package/dist/src/react/notifications/use-notifications.d.ts.map +1 -0
- package/dist/src/react/payments/AutoReloadToggle.d.ts +6 -0
- package/dist/src/react/payments/AutoReloadToggle.d.ts.map +1 -0
- package/dist/src/react/payments/Balance.d.ts +8 -0
- package/dist/src/react/payments/Balance.d.ts.map +1 -0
- package/dist/src/react/payments/CancelSubscription.d.ts +6 -0
- package/dist/src/react/payments/CancelSubscription.d.ts.map +1 -0
- package/dist/src/react/payments/CheckoutButton.d.ts +7 -0
- package/dist/src/react/payments/CheckoutButton.d.ts.map +1 -0
- package/dist/src/react/payments/CustomerPortalButton.d.ts +6 -0
- package/dist/src/react/payments/CustomerPortalButton.d.ts.map +1 -0
- package/dist/src/react/payments/FeatureMeter.d.ts +6 -0
- package/dist/src/react/payments/FeatureMeter.d.ts.map +1 -0
- package/dist/src/react/payments/Plans.d.ts +8 -0
- package/dist/src/react/payments/Plans.d.ts.map +1 -0
- package/dist/src/react/payments/TopUpButton.d.ts +8 -0
- package/dist/src/react/payments/TopUpButton.d.ts.map +1 -0
- package/dist/src/react/payments/UpdateBilling.d.ts +3 -0
- package/dist/src/react/payments/UpdateBilling.d.ts.map +1 -0
- package/dist/src/react/payments/UpgradePrompt.d.ts +8 -0
- package/dist/src/react/payments/UpgradePrompt.d.ts.map +1 -0
- package/dist/src/react/primitives/Skeleton.d.ts +15 -0
- package/dist/src/react/primitives/Skeleton.d.ts.map +1 -0
- package/dist/src/react/provider.d.ts +21 -0
- package/dist/src/react/provider.d.ts.map +1 -0
- package/dist/src/react/request.d.ts +2 -0
- package/dist/src/react/request.d.ts.map +1 -0
- package/dist/src/react/styles-entry.d.ts +4 -0
- package/dist/src/react/styles-entry.d.ts.map +1 -0
- package/dist/src/react/styles.d.ts +2 -0
- package/dist/src/react/styles.d.ts.map +1 -0
- package/dist/src/react/theme-entry.d.ts +3 -0
- package/dist/src/react/theme-entry.d.ts.map +1 -0
- package/dist/src/react/theme.d.ts +6 -0
- package/dist/src/react/theme.d.ts.map +1 -0
- package/dist/src/react/types.d.ts +191 -0
- package/dist/src/react/types.d.ts.map +1 -0
- package/dist/src/react/use-brokr-theme.d.ts +6 -0
- package/dist/src/react/use-brokr-theme.d.ts.map +1 -0
- package/dist/src/runtime.d.ts +69 -180
- package/dist/src/runtime.d.ts.map +1 -1
- package/dist/src/storage/client.d.ts +113 -0
- package/dist/src/storage/client.d.ts.map +1 -0
- package/dist/src/storage/types.d.ts +60 -0
- package/dist/src/storage/types.d.ts.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +70 -9
package/dist/next.mjs
ADDED
|
@@ -0,0 +1,2710 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
8
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
9
|
+
}) : x)(function(x) {
|
|
10
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
11
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
12
|
+
});
|
|
13
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
14
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
15
|
+
};
|
|
16
|
+
var __copyProps = (to, from, except, desc) => {
|
|
17
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
18
|
+
for (let key of __getOwnPropNames(from))
|
|
19
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
20
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
25
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
26
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
27
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
28
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
29
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
30
|
+
mod
|
|
31
|
+
));
|
|
32
|
+
|
|
33
|
+
// ../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/headers.js
|
|
34
|
+
var require_headers = __commonJS({
|
|
35
|
+
"../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/headers.js"(exports, module) {
|
|
36
|
+
"use strict";
|
|
37
|
+
var __defProp2 = Object.defineProperty;
|
|
38
|
+
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
39
|
+
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
40
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
41
|
+
var __export = (target, all) => {
|
|
42
|
+
for (var name in all)
|
|
43
|
+
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
44
|
+
};
|
|
45
|
+
var __copyProps2 = (to, from, except, desc) => {
|
|
46
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
47
|
+
for (let key of __getOwnPropNames2(from))
|
|
48
|
+
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
49
|
+
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
50
|
+
}
|
|
51
|
+
return to;
|
|
52
|
+
};
|
|
53
|
+
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
54
|
+
var headers_exports = {};
|
|
55
|
+
__export(headers_exports, {
|
|
56
|
+
CITY_HEADER_NAME: () => CITY_HEADER_NAME,
|
|
57
|
+
COUNTRY_HEADER_NAME: () => COUNTRY_HEADER_NAME,
|
|
58
|
+
EMOJI_FLAG_UNICODE_STARTING_POSITION: () => EMOJI_FLAG_UNICODE_STARTING_POSITION,
|
|
59
|
+
IP_HEADER_NAME: () => IP_HEADER_NAME,
|
|
60
|
+
LATITUDE_HEADER_NAME: () => LATITUDE_HEADER_NAME,
|
|
61
|
+
LONGITUDE_HEADER_NAME: () => LONGITUDE_HEADER_NAME,
|
|
62
|
+
POSTAL_CODE_HEADER_NAME: () => POSTAL_CODE_HEADER_NAME,
|
|
63
|
+
REGION_HEADER_NAME: () => REGION_HEADER_NAME,
|
|
64
|
+
REQUEST_ID_HEADER_NAME: () => REQUEST_ID_HEADER_NAME,
|
|
65
|
+
geolocation: () => geolocation2,
|
|
66
|
+
ipAddress: () => ipAddress2
|
|
67
|
+
});
|
|
68
|
+
module.exports = __toCommonJS(headers_exports);
|
|
69
|
+
var CITY_HEADER_NAME = "x-vercel-ip-city";
|
|
70
|
+
var COUNTRY_HEADER_NAME = "x-vercel-ip-country";
|
|
71
|
+
var IP_HEADER_NAME = "x-real-ip";
|
|
72
|
+
var LATITUDE_HEADER_NAME = "x-vercel-ip-latitude";
|
|
73
|
+
var LONGITUDE_HEADER_NAME = "x-vercel-ip-longitude";
|
|
74
|
+
var REGION_HEADER_NAME = "x-vercel-ip-country-region";
|
|
75
|
+
var POSTAL_CODE_HEADER_NAME = "x-vercel-ip-postal-code";
|
|
76
|
+
var REQUEST_ID_HEADER_NAME = "x-vercel-id";
|
|
77
|
+
var EMOJI_FLAG_UNICODE_STARTING_POSITION = 127397;
|
|
78
|
+
function getHeader(headers, key) {
|
|
79
|
+
return headers.get(key) ?? void 0;
|
|
80
|
+
}
|
|
81
|
+
function getHeaderWithDecode(request, key) {
|
|
82
|
+
const header = getHeader(request.headers, key);
|
|
83
|
+
return header ? decodeURIComponent(header) : void 0;
|
|
84
|
+
}
|
|
85
|
+
function getFlag(countryCode) {
|
|
86
|
+
const regex = new RegExp("^[A-Z]{2}$").test(countryCode);
|
|
87
|
+
if (!countryCode || !regex)
|
|
88
|
+
return void 0;
|
|
89
|
+
return String.fromCodePoint(
|
|
90
|
+
...countryCode.split("").map((char) => EMOJI_FLAG_UNICODE_STARTING_POSITION + char.charCodeAt(0))
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
function ipAddress2(input) {
|
|
94
|
+
const headers = "headers" in input ? input.headers : input;
|
|
95
|
+
return getHeader(headers, IP_HEADER_NAME);
|
|
96
|
+
}
|
|
97
|
+
function getRegionFromRequestId(requestId) {
|
|
98
|
+
if (!requestId) {
|
|
99
|
+
return "dev1";
|
|
100
|
+
}
|
|
101
|
+
return requestId.split(":")[0];
|
|
102
|
+
}
|
|
103
|
+
function geolocation2(request) {
|
|
104
|
+
return {
|
|
105
|
+
// city name may be encoded to support multi-byte characters
|
|
106
|
+
city: getHeaderWithDecode(request, CITY_HEADER_NAME),
|
|
107
|
+
country: getHeader(request.headers, COUNTRY_HEADER_NAME),
|
|
108
|
+
flag: getFlag(getHeader(request.headers, COUNTRY_HEADER_NAME)),
|
|
109
|
+
countryRegion: getHeader(request.headers, REGION_HEADER_NAME),
|
|
110
|
+
region: getRegionFromRequestId(
|
|
111
|
+
getHeader(request.headers, REQUEST_ID_HEADER_NAME)
|
|
112
|
+
),
|
|
113
|
+
latitude: getHeader(request.headers, LATITUDE_HEADER_NAME),
|
|
114
|
+
longitude: getHeader(request.headers, LONGITUDE_HEADER_NAME),
|
|
115
|
+
postalCode: getHeader(request.headers, POSTAL_CODE_HEADER_NAME)
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// ../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/get-env.js
|
|
122
|
+
var require_get_env = __commonJS({
|
|
123
|
+
"../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/get-env.js"(exports, module) {
|
|
124
|
+
"use strict";
|
|
125
|
+
var __defProp2 = Object.defineProperty;
|
|
126
|
+
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
127
|
+
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
128
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
129
|
+
var __export = (target, all) => {
|
|
130
|
+
for (var name in all)
|
|
131
|
+
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
132
|
+
};
|
|
133
|
+
var __copyProps2 = (to, from, except, desc) => {
|
|
134
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
135
|
+
for (let key of __getOwnPropNames2(from))
|
|
136
|
+
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
137
|
+
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
138
|
+
}
|
|
139
|
+
return to;
|
|
140
|
+
};
|
|
141
|
+
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
142
|
+
var get_env_exports = {};
|
|
143
|
+
__export(get_env_exports, {
|
|
144
|
+
getEnv: () => getEnv2
|
|
145
|
+
});
|
|
146
|
+
module.exports = __toCommonJS(get_env_exports);
|
|
147
|
+
var getEnv2 = (env = process.env) => ({
|
|
148
|
+
/**
|
|
149
|
+
* An indicator to show that System Environment Variables have been exposed to your project's Deployments.
|
|
150
|
+
* @example "1"
|
|
151
|
+
*/
|
|
152
|
+
VERCEL: get(env, "VERCEL"),
|
|
153
|
+
/**
|
|
154
|
+
* An indicator that the code is running in a Continuous Integration environment.
|
|
155
|
+
* @example "1"
|
|
156
|
+
*/
|
|
157
|
+
CI: get(env, "CI"),
|
|
158
|
+
/**
|
|
159
|
+
* The Environment that the app is deployed and running on.
|
|
160
|
+
* @example "production"
|
|
161
|
+
*/
|
|
162
|
+
VERCEL_ENV: get(env, "VERCEL_ENV"),
|
|
163
|
+
/**
|
|
164
|
+
* The domain name of the generated deployment URL. The value does not include the protocol scheme https://.
|
|
165
|
+
* NOTE: This Variable cannot be used in conjunction with Standard Deployment Protection.
|
|
166
|
+
* @example "*.vercel.app"
|
|
167
|
+
*/
|
|
168
|
+
VERCEL_URL: get(env, "VERCEL_URL"),
|
|
169
|
+
/**
|
|
170
|
+
* The domain name of the generated Git branch URL. The value does not include the protocol scheme https://.
|
|
171
|
+
* @example "*-git-*.vercel.app"
|
|
172
|
+
*/
|
|
173
|
+
VERCEL_BRANCH_URL: get(env, "VERCEL_BRANCH_URL"),
|
|
174
|
+
/**
|
|
175
|
+
* A production domain name of the project. This is useful to reliably generate links that point to production such as OG-image URLs.
|
|
176
|
+
* The value does not include the protocol scheme https://.
|
|
177
|
+
* @example "myproject.vercel.app"
|
|
178
|
+
*/
|
|
179
|
+
VERCEL_PROJECT_PRODUCTION_URL: get(env, "VERCEL_PROJECT_PRODUCTION_URL"),
|
|
180
|
+
/**
|
|
181
|
+
* The ID of the Region where the app is running.
|
|
182
|
+
*
|
|
183
|
+
* Possible values:
|
|
184
|
+
* - arn1 (Stockholm, Sweden)
|
|
185
|
+
* - bom1 (Mumbai, India)
|
|
186
|
+
* - cdg1 (Paris, France)
|
|
187
|
+
* - cle1 (Cleveland, USA)
|
|
188
|
+
* - cpt1 (Cape Town, South Africa)
|
|
189
|
+
* - dub1 (Dublin, Ireland)
|
|
190
|
+
* - fra1 (Frankfurt, Germany)
|
|
191
|
+
* - gru1 (São Paulo, Brazil)
|
|
192
|
+
* - hkg1 (Hong Kong)
|
|
193
|
+
* - hnd1 (Tokyo, Japan)
|
|
194
|
+
* - iad1 (Washington, D.C., USA)
|
|
195
|
+
* - icn1 (Seoul, South Korea)
|
|
196
|
+
* - kix1 (Osaka, Japan)
|
|
197
|
+
* - lhr1 (London, United Kingdom)
|
|
198
|
+
* - pdx1 (Portland, USA)
|
|
199
|
+
* - sfo1 (San Francisco, USA)
|
|
200
|
+
* - sin1 (Singapore)
|
|
201
|
+
* - syd1 (Sydney, Australia)
|
|
202
|
+
* - dev1 (Development Region)
|
|
203
|
+
*
|
|
204
|
+
* @example "iad1"
|
|
205
|
+
*/
|
|
206
|
+
VERCEL_REGION: get(env, "VERCEL_REGION"),
|
|
207
|
+
/**
|
|
208
|
+
* The unique identifier for the deployment, which can be used to implement Skew Protection.
|
|
209
|
+
* @example "dpl_7Gw5ZMBpQA8h9GF832KGp7nwbuh3"
|
|
210
|
+
*/
|
|
211
|
+
VERCEL_DEPLOYMENT_ID: get(env, "VERCEL_DEPLOYMENT_ID"),
|
|
212
|
+
/**
|
|
213
|
+
* When Skew Protection is enabled in Project Settings, this value is set to 1.
|
|
214
|
+
* @example "1"
|
|
215
|
+
*/
|
|
216
|
+
VERCEL_SKEW_PROTECTION_ENABLED: get(env, "VERCEL_SKEW_PROTECTION_ENABLED"),
|
|
217
|
+
/**
|
|
218
|
+
* The Protection Bypass for Automation value, if the secret has been generated in the project's Deployment Protection settings.
|
|
219
|
+
*/
|
|
220
|
+
VERCEL_AUTOMATION_BYPASS_SECRET: get(env, "VERCEL_AUTOMATION_BYPASS_SECRET"),
|
|
221
|
+
/**
|
|
222
|
+
* The Git Provider the deployment is triggered from.
|
|
223
|
+
* @example "github"
|
|
224
|
+
*/
|
|
225
|
+
VERCEL_GIT_PROVIDER: get(env, "VERCEL_GIT_PROVIDER"),
|
|
226
|
+
/**
|
|
227
|
+
* The origin repository the deployment is triggered from.
|
|
228
|
+
* @example "my-site"
|
|
229
|
+
*/
|
|
230
|
+
VERCEL_GIT_REPO_SLUG: get(env, "VERCEL_GIT_REPO_SLUG"),
|
|
231
|
+
/**
|
|
232
|
+
* The account that owns the repository the deployment is triggered from.
|
|
233
|
+
* @example "acme"
|
|
234
|
+
*/
|
|
235
|
+
VERCEL_GIT_REPO_OWNER: get(env, "VERCEL_GIT_REPO_OWNER"),
|
|
236
|
+
/**
|
|
237
|
+
* The ID of the repository the deployment is triggered from.
|
|
238
|
+
* @example "117716146"
|
|
239
|
+
*/
|
|
240
|
+
VERCEL_GIT_REPO_ID: get(env, "VERCEL_GIT_REPO_ID"),
|
|
241
|
+
/**
|
|
242
|
+
* The git branch of the commit the deployment was triggered by.
|
|
243
|
+
* @example "improve-about-page"
|
|
244
|
+
*/
|
|
245
|
+
VERCEL_GIT_COMMIT_REF: get(env, "VERCEL_GIT_COMMIT_REF"),
|
|
246
|
+
/**
|
|
247
|
+
* The git SHA of the commit the deployment was triggered by.
|
|
248
|
+
* @example "fa1eade47b73733d6312d5abfad33ce9e4068081"
|
|
249
|
+
*/
|
|
250
|
+
VERCEL_GIT_COMMIT_SHA: get(env, "VERCEL_GIT_COMMIT_SHA"),
|
|
251
|
+
/**
|
|
252
|
+
* The message attached to the commit the deployment was triggered by.
|
|
253
|
+
* @example "Update about page"
|
|
254
|
+
*/
|
|
255
|
+
VERCEL_GIT_COMMIT_MESSAGE: get(env, "VERCEL_GIT_COMMIT_MESSAGE"),
|
|
256
|
+
/**
|
|
257
|
+
* The username attached to the author of the commit that the project was deployed by.
|
|
258
|
+
* @example "johndoe"
|
|
259
|
+
*/
|
|
260
|
+
VERCEL_GIT_COMMIT_AUTHOR_LOGIN: get(env, "VERCEL_GIT_COMMIT_AUTHOR_LOGIN"),
|
|
261
|
+
/**
|
|
262
|
+
* The name attached to the author of the commit that the project was deployed by.
|
|
263
|
+
* @example "John Doe"
|
|
264
|
+
*/
|
|
265
|
+
VERCEL_GIT_COMMIT_AUTHOR_NAME: get(env, "VERCEL_GIT_COMMIT_AUTHOR_NAME"),
|
|
266
|
+
/**
|
|
267
|
+
* The git SHA of the last successful deployment for the project and branch.
|
|
268
|
+
* NOTE: This Variable is only exposed when an Ignored Build Step is provided.
|
|
269
|
+
* @example "fa1eade47b73733d6312d5abfad33ce9e4068080"
|
|
270
|
+
*/
|
|
271
|
+
VERCEL_GIT_PREVIOUS_SHA: get(env, "VERCEL_GIT_PREVIOUS_SHA"),
|
|
272
|
+
/**
|
|
273
|
+
* The pull request id the deployment was triggered by. If a deployment is created on a branch before a pull request is made, this value will be an empty string.
|
|
274
|
+
* @example "23"
|
|
275
|
+
*/
|
|
276
|
+
VERCEL_GIT_PULL_REQUEST_ID: get(env, "VERCEL_GIT_PULL_REQUEST_ID")
|
|
277
|
+
});
|
|
278
|
+
var get = (env, key) => {
|
|
279
|
+
const value = env[key];
|
|
280
|
+
return value === "" ? void 0 : value;
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// ../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/get-context.js
|
|
286
|
+
var require_get_context = __commonJS({
|
|
287
|
+
"../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/get-context.js"(exports, module) {
|
|
288
|
+
"use strict";
|
|
289
|
+
var __defProp2 = Object.defineProperty;
|
|
290
|
+
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
291
|
+
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
292
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
293
|
+
var __export = (target, all) => {
|
|
294
|
+
for (var name in all)
|
|
295
|
+
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
296
|
+
};
|
|
297
|
+
var __copyProps2 = (to, from, except, desc) => {
|
|
298
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
299
|
+
for (let key of __getOwnPropNames2(from))
|
|
300
|
+
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
301
|
+
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
302
|
+
}
|
|
303
|
+
return to;
|
|
304
|
+
};
|
|
305
|
+
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
306
|
+
var get_context_exports = {};
|
|
307
|
+
__export(get_context_exports, {
|
|
308
|
+
SYMBOL_FOR_REQ_CONTEXT: () => SYMBOL_FOR_REQ_CONTEXT,
|
|
309
|
+
getContext: () => getContext
|
|
310
|
+
});
|
|
311
|
+
module.exports = __toCommonJS(get_context_exports);
|
|
312
|
+
var SYMBOL_FOR_REQ_CONTEXT = Symbol.for("@vercel/request-context");
|
|
313
|
+
function getContext() {
|
|
314
|
+
const fromSymbol = globalThis;
|
|
315
|
+
return fromSymbol[SYMBOL_FOR_REQ_CONTEXT]?.get?.() ?? {};
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// ../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/wait-until.js
|
|
321
|
+
var require_wait_until = __commonJS({
|
|
322
|
+
"../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/wait-until.js"(exports, module) {
|
|
323
|
+
"use strict";
|
|
324
|
+
var __defProp2 = Object.defineProperty;
|
|
325
|
+
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
326
|
+
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
327
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
328
|
+
var __export = (target, all) => {
|
|
329
|
+
for (var name in all)
|
|
330
|
+
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
331
|
+
};
|
|
332
|
+
var __copyProps2 = (to, from, except, desc) => {
|
|
333
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
334
|
+
for (let key of __getOwnPropNames2(from))
|
|
335
|
+
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
336
|
+
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
337
|
+
}
|
|
338
|
+
return to;
|
|
339
|
+
};
|
|
340
|
+
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
341
|
+
var wait_until_exports = {};
|
|
342
|
+
__export(wait_until_exports, {
|
|
343
|
+
waitUntil: () => waitUntil2
|
|
344
|
+
});
|
|
345
|
+
module.exports = __toCommonJS(wait_until_exports);
|
|
346
|
+
var import_get_context = require_get_context();
|
|
347
|
+
var waitUntil2 = (promise) => {
|
|
348
|
+
if (promise === null || typeof promise !== "object" || typeof promise.then !== "function") {
|
|
349
|
+
throw new TypeError(
|
|
350
|
+
`waitUntil can only be called with a Promise, got ${typeof promise}`
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
return (0, import_get_context.getContext)().waitUntil?.(promise);
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
// ../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/middleware.js
|
|
359
|
+
var require_middleware = __commonJS({
|
|
360
|
+
"../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/middleware.js"(exports, module) {
|
|
361
|
+
"use strict";
|
|
362
|
+
var __defProp2 = Object.defineProperty;
|
|
363
|
+
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
364
|
+
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
365
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
366
|
+
var __export = (target, all) => {
|
|
367
|
+
for (var name in all)
|
|
368
|
+
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
369
|
+
};
|
|
370
|
+
var __copyProps2 = (to, from, except, desc) => {
|
|
371
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
372
|
+
for (let key of __getOwnPropNames2(from))
|
|
373
|
+
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
374
|
+
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
375
|
+
}
|
|
376
|
+
return to;
|
|
377
|
+
};
|
|
378
|
+
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
379
|
+
var middleware_exports = {};
|
|
380
|
+
__export(middleware_exports, {
|
|
381
|
+
next: () => next2,
|
|
382
|
+
rewrite: () => rewrite2
|
|
383
|
+
});
|
|
384
|
+
module.exports = __toCommonJS(middleware_exports);
|
|
385
|
+
function handleMiddlewareField(init, headers) {
|
|
386
|
+
if (init?.request?.headers) {
|
|
387
|
+
if (!(init.request.headers instanceof Headers)) {
|
|
388
|
+
throw new Error("request.headers must be an instance of Headers");
|
|
389
|
+
}
|
|
390
|
+
const keys = [];
|
|
391
|
+
for (const [key, value] of init.request.headers) {
|
|
392
|
+
headers.set("x-middleware-request-" + key, value);
|
|
393
|
+
keys.push(key);
|
|
394
|
+
}
|
|
395
|
+
headers.set("x-middleware-override-headers", keys.join(","));
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
function rewrite2(destination, init) {
|
|
399
|
+
const headers = new Headers(init?.headers ?? {});
|
|
400
|
+
headers.set("x-middleware-rewrite", String(destination));
|
|
401
|
+
handleMiddlewareField(init, headers);
|
|
402
|
+
return new Response(null, {
|
|
403
|
+
...init,
|
|
404
|
+
headers
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
function next2(init) {
|
|
408
|
+
const headers = new Headers(init?.headers ?? {});
|
|
409
|
+
headers.set("x-middleware-next", "1");
|
|
410
|
+
handleMiddlewareField(init, headers);
|
|
411
|
+
return new Response(null, {
|
|
412
|
+
...init,
|
|
413
|
+
headers
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
// ../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/cache/in-memory-cache.js
|
|
420
|
+
var require_in_memory_cache = __commonJS({
|
|
421
|
+
"../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/cache/in-memory-cache.js"(exports, module) {
|
|
422
|
+
"use strict";
|
|
423
|
+
var __defProp2 = Object.defineProperty;
|
|
424
|
+
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
425
|
+
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
426
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
427
|
+
var __export = (target, all) => {
|
|
428
|
+
for (var name in all)
|
|
429
|
+
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
430
|
+
};
|
|
431
|
+
var __copyProps2 = (to, from, except, desc) => {
|
|
432
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
433
|
+
for (let key of __getOwnPropNames2(from))
|
|
434
|
+
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
435
|
+
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
436
|
+
}
|
|
437
|
+
return to;
|
|
438
|
+
};
|
|
439
|
+
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
440
|
+
var in_memory_cache_exports = {};
|
|
441
|
+
__export(in_memory_cache_exports, {
|
|
442
|
+
InMemoryCache: () => InMemoryCache
|
|
443
|
+
});
|
|
444
|
+
module.exports = __toCommonJS(in_memory_cache_exports);
|
|
445
|
+
var InMemoryCache = class {
|
|
446
|
+
constructor() {
|
|
447
|
+
this.cache = {};
|
|
448
|
+
}
|
|
449
|
+
async get(key) {
|
|
450
|
+
const entry = this.cache[key];
|
|
451
|
+
if (entry) {
|
|
452
|
+
if (entry.ttl && entry.lastModified + entry.ttl * 1e3 < Date.now()) {
|
|
453
|
+
await this.delete(key);
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
return JSON.parse(entry.value);
|
|
457
|
+
}
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
async set(key, value, options) {
|
|
461
|
+
const serialized = JSON.stringify(value ?? null);
|
|
462
|
+
this.cache[key] = {
|
|
463
|
+
value: serialized,
|
|
464
|
+
lastModified: Date.now(),
|
|
465
|
+
ttl: options?.ttl,
|
|
466
|
+
tags: new Set(options?.tags || [])
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
async delete(key) {
|
|
470
|
+
delete this.cache[key];
|
|
471
|
+
}
|
|
472
|
+
async expireTag(tag) {
|
|
473
|
+
const tags = Array.isArray(tag) ? tag : [tag];
|
|
474
|
+
for (const key in this.cache) {
|
|
475
|
+
if (Object.prototype.hasOwnProperty.call(this.cache, key)) {
|
|
476
|
+
const entry = this.cache[key];
|
|
477
|
+
if (tags.some((t) => entry.tags.has(t))) {
|
|
478
|
+
delete this.cache[key];
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
// ../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/cache/build-client.js
|
|
488
|
+
var require_build_client = __commonJS({
|
|
489
|
+
"../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/cache/build-client.js"(exports, module) {
|
|
490
|
+
"use strict";
|
|
491
|
+
var __defProp2 = Object.defineProperty;
|
|
492
|
+
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
493
|
+
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
494
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
495
|
+
var __export = (target, all) => {
|
|
496
|
+
for (var name in all)
|
|
497
|
+
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
498
|
+
};
|
|
499
|
+
var __copyProps2 = (to, from, except, desc) => {
|
|
500
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
501
|
+
for (let key of __getOwnPropNames2(from))
|
|
502
|
+
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
503
|
+
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
504
|
+
}
|
|
505
|
+
return to;
|
|
506
|
+
};
|
|
507
|
+
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
508
|
+
var build_client_exports = {};
|
|
509
|
+
__export(build_client_exports, {
|
|
510
|
+
BuildCache: () => BuildCache
|
|
511
|
+
});
|
|
512
|
+
module.exports = __toCommonJS(build_client_exports);
|
|
513
|
+
var import_index = require_cache();
|
|
514
|
+
var BuildCache = class {
|
|
515
|
+
constructor({
|
|
516
|
+
endpoint,
|
|
517
|
+
headers,
|
|
518
|
+
onError,
|
|
519
|
+
timeout = 500
|
|
520
|
+
}) {
|
|
521
|
+
this.get = async (key) => {
|
|
522
|
+
const controller = new AbortController();
|
|
523
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
524
|
+
try {
|
|
525
|
+
const res = await fetch(`${this.endpoint}${key}`, {
|
|
526
|
+
headers: this.headers,
|
|
527
|
+
method: "GET",
|
|
528
|
+
signal: controller.signal
|
|
529
|
+
});
|
|
530
|
+
if (res.status === 404) {
|
|
531
|
+
clearTimeout(timeoutId);
|
|
532
|
+
return null;
|
|
533
|
+
}
|
|
534
|
+
if (res.status === 200) {
|
|
535
|
+
const cacheState = res.headers.get(
|
|
536
|
+
import_index.HEADERS_VERCEL_CACHE_STATE
|
|
537
|
+
);
|
|
538
|
+
if (cacheState !== import_index.PkgCacheState.Fresh) {
|
|
539
|
+
res.body?.cancel?.();
|
|
540
|
+
clearTimeout(timeoutId);
|
|
541
|
+
return null;
|
|
542
|
+
}
|
|
543
|
+
const result = await res.json();
|
|
544
|
+
clearTimeout(timeoutId);
|
|
545
|
+
return result;
|
|
546
|
+
} else {
|
|
547
|
+
clearTimeout(timeoutId);
|
|
548
|
+
throw new Error(`Failed to get cache: ${res.statusText}`);
|
|
549
|
+
}
|
|
550
|
+
} catch (error) {
|
|
551
|
+
clearTimeout(timeoutId);
|
|
552
|
+
if (error.name === "AbortError") {
|
|
553
|
+
const timeoutError = new Error(
|
|
554
|
+
`Cache request timed out after ${this.timeout}ms`
|
|
555
|
+
);
|
|
556
|
+
timeoutError.stack = error.stack;
|
|
557
|
+
this.onError?.(timeoutError);
|
|
558
|
+
} else {
|
|
559
|
+
this.onError?.(error);
|
|
560
|
+
}
|
|
561
|
+
return null;
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
this.set = async (key, value, options) => {
|
|
565
|
+
const controller = new AbortController();
|
|
566
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
567
|
+
try {
|
|
568
|
+
const optionalHeaders = {};
|
|
569
|
+
if (options?.ttl) {
|
|
570
|
+
optionalHeaders[import_index.HEADERS_VERCEL_REVALIDATE] = options.ttl.toString();
|
|
571
|
+
}
|
|
572
|
+
if (options?.tags && options.tags.length > 0) {
|
|
573
|
+
optionalHeaders[import_index.HEADERS_VERCEL_CACHE_TAGS] = options.tags.join(",");
|
|
574
|
+
}
|
|
575
|
+
if (options?.name) {
|
|
576
|
+
optionalHeaders[import_index.HEADERS_VERCEL_CACHE_ITEM_NAME] = options.name;
|
|
577
|
+
}
|
|
578
|
+
const res = await fetch(`${this.endpoint}${key}`, {
|
|
579
|
+
method: "POST",
|
|
580
|
+
headers: {
|
|
581
|
+
...this.headers,
|
|
582
|
+
...optionalHeaders
|
|
583
|
+
},
|
|
584
|
+
body: JSON.stringify(value),
|
|
585
|
+
signal: controller.signal
|
|
586
|
+
});
|
|
587
|
+
clearTimeout(timeoutId);
|
|
588
|
+
if (res.status !== 200) {
|
|
589
|
+
throw new Error(`Failed to set cache: ${res.status} ${res.statusText}`);
|
|
590
|
+
}
|
|
591
|
+
} catch (error) {
|
|
592
|
+
clearTimeout(timeoutId);
|
|
593
|
+
if (error.name === "AbortError") {
|
|
594
|
+
const timeoutError = new Error(
|
|
595
|
+
`Cache request timed out after ${this.timeout}ms`
|
|
596
|
+
);
|
|
597
|
+
timeoutError.stack = error.stack;
|
|
598
|
+
this.onError?.(timeoutError);
|
|
599
|
+
} else {
|
|
600
|
+
this.onError?.(error);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
this.delete = async (key) => {
|
|
605
|
+
const controller = new AbortController();
|
|
606
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
607
|
+
try {
|
|
608
|
+
const res = await fetch(`${this.endpoint}${key}`, {
|
|
609
|
+
method: "DELETE",
|
|
610
|
+
headers: this.headers,
|
|
611
|
+
signal: controller.signal
|
|
612
|
+
});
|
|
613
|
+
clearTimeout(timeoutId);
|
|
614
|
+
if (res.status !== 200) {
|
|
615
|
+
throw new Error(`Failed to delete cache: ${res.statusText}`);
|
|
616
|
+
}
|
|
617
|
+
} catch (error) {
|
|
618
|
+
clearTimeout(timeoutId);
|
|
619
|
+
if (error.name === "AbortError") {
|
|
620
|
+
const timeoutError = new Error(
|
|
621
|
+
`Cache request timed out after ${this.timeout}ms`
|
|
622
|
+
);
|
|
623
|
+
timeoutError.stack = error.stack;
|
|
624
|
+
this.onError?.(timeoutError);
|
|
625
|
+
} else {
|
|
626
|
+
this.onError?.(error);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
this.expireTag = async (tag) => {
|
|
631
|
+
const controller = new AbortController();
|
|
632
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
633
|
+
try {
|
|
634
|
+
if (Array.isArray(tag)) {
|
|
635
|
+
tag = tag.join(",");
|
|
636
|
+
}
|
|
637
|
+
const res = await fetch(`${this.endpoint}revalidate?tags=${tag}`, {
|
|
638
|
+
method: "POST",
|
|
639
|
+
headers: this.headers,
|
|
640
|
+
signal: controller.signal
|
|
641
|
+
});
|
|
642
|
+
clearTimeout(timeoutId);
|
|
643
|
+
if (res.status !== 200) {
|
|
644
|
+
throw new Error(`Failed to revalidate tag: ${res.statusText}`);
|
|
645
|
+
}
|
|
646
|
+
} catch (error) {
|
|
647
|
+
clearTimeout(timeoutId);
|
|
648
|
+
if (error.name === "AbortError") {
|
|
649
|
+
const timeoutError = new Error(
|
|
650
|
+
`Cache request timed out after ${this.timeout}ms`
|
|
651
|
+
);
|
|
652
|
+
timeoutError.stack = error.stack;
|
|
653
|
+
this.onError?.(timeoutError);
|
|
654
|
+
} else {
|
|
655
|
+
this.onError?.(error);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
this.endpoint = endpoint;
|
|
660
|
+
this.headers = headers;
|
|
661
|
+
this.onError = onError;
|
|
662
|
+
this.timeout = timeout;
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
// ../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/cache/index.js
|
|
669
|
+
var require_cache = __commonJS({
|
|
670
|
+
"../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/cache/index.js"(exports, module) {
|
|
671
|
+
"use strict";
|
|
672
|
+
var __defProp2 = Object.defineProperty;
|
|
673
|
+
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
674
|
+
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
675
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
676
|
+
var __export = (target, all) => {
|
|
677
|
+
for (var name in all)
|
|
678
|
+
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
679
|
+
};
|
|
680
|
+
var __copyProps2 = (to, from, except, desc) => {
|
|
681
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
682
|
+
for (let key of __getOwnPropNames2(from))
|
|
683
|
+
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
684
|
+
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
685
|
+
}
|
|
686
|
+
return to;
|
|
687
|
+
};
|
|
688
|
+
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
689
|
+
var cache_exports = {};
|
|
690
|
+
__export(cache_exports, {
|
|
691
|
+
HEADERS_VERCEL_CACHE_ITEM_NAME: () => HEADERS_VERCEL_CACHE_ITEM_NAME,
|
|
692
|
+
HEADERS_VERCEL_CACHE_STATE: () => HEADERS_VERCEL_CACHE_STATE,
|
|
693
|
+
HEADERS_VERCEL_CACHE_TAGS: () => HEADERS_VERCEL_CACHE_TAGS,
|
|
694
|
+
HEADERS_VERCEL_REVALIDATE: () => HEADERS_VERCEL_REVALIDATE,
|
|
695
|
+
PkgCacheState: () => PkgCacheState,
|
|
696
|
+
getCache: () => getCache2
|
|
697
|
+
});
|
|
698
|
+
module.exports = __toCommonJS(cache_exports);
|
|
699
|
+
var import_get_context = require_get_context();
|
|
700
|
+
var import_in_memory_cache = require_in_memory_cache();
|
|
701
|
+
var import_build_client = require_build_client();
|
|
702
|
+
var defaultKeyHashFunction = (key) => {
|
|
703
|
+
let hash = 5381;
|
|
704
|
+
for (let i = 0; i < key.length; i++) {
|
|
705
|
+
hash = hash * 33 ^ key.charCodeAt(i);
|
|
706
|
+
}
|
|
707
|
+
return (hash >>> 0).toString(16);
|
|
708
|
+
};
|
|
709
|
+
var defaultNamespaceSeparator = "$";
|
|
710
|
+
var inMemoryCacheInstance = null;
|
|
711
|
+
var buildCacheInstance = null;
|
|
712
|
+
var getCache2 = (cacheOptions) => {
|
|
713
|
+
const resolveCache = () => {
|
|
714
|
+
let cache2;
|
|
715
|
+
if ((0, import_get_context.getContext)().cache) {
|
|
716
|
+
cache2 = (0, import_get_context.getContext)().cache;
|
|
717
|
+
} else {
|
|
718
|
+
cache2 = getCacheImplementation(
|
|
719
|
+
process.env.SUSPENSE_CACHE_DEBUG === "true"
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
return cache2;
|
|
723
|
+
};
|
|
724
|
+
return wrapWithKeyTransformation(
|
|
725
|
+
resolveCache,
|
|
726
|
+
createKeyTransformer(cacheOptions)
|
|
727
|
+
);
|
|
728
|
+
};
|
|
729
|
+
function createKeyTransformer(cacheOptions) {
|
|
730
|
+
const hashFunction = cacheOptions?.keyHashFunction || defaultKeyHashFunction;
|
|
731
|
+
return (key) => {
|
|
732
|
+
if (!cacheOptions?.namespace)
|
|
733
|
+
return hashFunction(key);
|
|
734
|
+
const separator = cacheOptions.namespaceSeparator || defaultNamespaceSeparator;
|
|
735
|
+
return `${cacheOptions.namespace}${separator}${hashFunction(key)}`;
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
function wrapWithKeyTransformation(resolveCache, makeKey) {
|
|
739
|
+
return {
|
|
740
|
+
get: (key) => {
|
|
741
|
+
return resolveCache().get(makeKey(key));
|
|
742
|
+
},
|
|
743
|
+
set: (key, value, options) => {
|
|
744
|
+
return resolveCache().set(makeKey(key), value, options);
|
|
745
|
+
},
|
|
746
|
+
delete: (key) => {
|
|
747
|
+
return resolveCache().delete(makeKey(key));
|
|
748
|
+
},
|
|
749
|
+
expireTag: (tag) => {
|
|
750
|
+
return resolveCache().expireTag(tag);
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
var warnedCacheUnavailable = false;
|
|
755
|
+
function getCacheImplementation(debug) {
|
|
756
|
+
if (!inMemoryCacheInstance) {
|
|
757
|
+
inMemoryCacheInstance = new import_in_memory_cache.InMemoryCache();
|
|
758
|
+
}
|
|
759
|
+
if (process.env.RUNTIME_CACHE_DISABLE_BUILD_CACHE === "true") {
|
|
760
|
+
debug && console.log("Using InMemoryCache as build cache is disabled");
|
|
761
|
+
return inMemoryCacheInstance;
|
|
762
|
+
}
|
|
763
|
+
const { RUNTIME_CACHE_ENDPOINT, RUNTIME_CACHE_HEADERS } = process.env;
|
|
764
|
+
if (debug) {
|
|
765
|
+
console.log("Runtime cache environment variables:", {
|
|
766
|
+
RUNTIME_CACHE_ENDPOINT,
|
|
767
|
+
RUNTIME_CACHE_HEADERS
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
if (!RUNTIME_CACHE_ENDPOINT || !RUNTIME_CACHE_HEADERS) {
|
|
771
|
+
if (!warnedCacheUnavailable) {
|
|
772
|
+
console.warn(
|
|
773
|
+
"Runtime Cache unavailable in this environment. Falling back to in-memory cache."
|
|
774
|
+
);
|
|
775
|
+
warnedCacheUnavailable = true;
|
|
776
|
+
}
|
|
777
|
+
return inMemoryCacheInstance;
|
|
778
|
+
}
|
|
779
|
+
if (!buildCacheInstance) {
|
|
780
|
+
let parsedHeaders = {};
|
|
781
|
+
try {
|
|
782
|
+
parsedHeaders = JSON.parse(RUNTIME_CACHE_HEADERS);
|
|
783
|
+
} catch (e) {
|
|
784
|
+
console.error("Failed to parse RUNTIME_CACHE_HEADERS:", e);
|
|
785
|
+
return inMemoryCacheInstance;
|
|
786
|
+
}
|
|
787
|
+
let timeout = 500;
|
|
788
|
+
if (process.env.RUNTIME_CACHE_TIMEOUT) {
|
|
789
|
+
const parsed = parseInt(process.env.RUNTIME_CACHE_TIMEOUT, 10);
|
|
790
|
+
if (!isNaN(parsed) && parsed > 0) {
|
|
791
|
+
timeout = parsed;
|
|
792
|
+
} else {
|
|
793
|
+
console.warn(
|
|
794
|
+
`Invalid RUNTIME_CACHE_TIMEOUT value: "${process.env.RUNTIME_CACHE_TIMEOUT}". Using default: ${timeout}ms`
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
buildCacheInstance = new import_build_client.BuildCache({
|
|
799
|
+
endpoint: RUNTIME_CACHE_ENDPOINT,
|
|
800
|
+
headers: parsedHeaders,
|
|
801
|
+
onError: (error) => console.error(error),
|
|
802
|
+
timeout
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
return buildCacheInstance;
|
|
806
|
+
}
|
|
807
|
+
var PkgCacheState = /* @__PURE__ */ ((PkgCacheState2) => {
|
|
808
|
+
PkgCacheState2["Fresh"] = "fresh";
|
|
809
|
+
PkgCacheState2["Stale"] = "stale";
|
|
810
|
+
PkgCacheState2["Expired"] = "expired";
|
|
811
|
+
PkgCacheState2["NotFound"] = "notFound";
|
|
812
|
+
PkgCacheState2["Error"] = "error";
|
|
813
|
+
return PkgCacheState2;
|
|
814
|
+
})(PkgCacheState || {});
|
|
815
|
+
var HEADERS_VERCEL_CACHE_STATE = "x-vercel-cache-state";
|
|
816
|
+
var HEADERS_VERCEL_REVALIDATE = "x-vercel-revalidate";
|
|
817
|
+
var HEADERS_VERCEL_CACHE_TAGS = "x-vercel-cache-tags";
|
|
818
|
+
var HEADERS_VERCEL_CACHE_ITEM_NAME = "x-vercel-cache-item-name";
|
|
819
|
+
}
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
// ../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/db-connections/index.js
|
|
823
|
+
var require_db_connections = __commonJS({
|
|
824
|
+
"../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/db-connections/index.js"(exports, module) {
|
|
825
|
+
"use strict";
|
|
826
|
+
var __defProp2 = Object.defineProperty;
|
|
827
|
+
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
828
|
+
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
829
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
830
|
+
var __export = (target, all) => {
|
|
831
|
+
for (var name in all)
|
|
832
|
+
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
833
|
+
};
|
|
834
|
+
var __copyProps2 = (to, from, except, desc) => {
|
|
835
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
836
|
+
for (let key of __getOwnPropNames2(from))
|
|
837
|
+
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
838
|
+
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
839
|
+
}
|
|
840
|
+
return to;
|
|
841
|
+
};
|
|
842
|
+
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
843
|
+
var db_connections_exports = {};
|
|
844
|
+
__export(db_connections_exports, {
|
|
845
|
+
attachDatabasePool: () => attachDatabasePool2,
|
|
846
|
+
experimental_attachDatabasePool: () => experimental_attachDatabasePool2
|
|
847
|
+
});
|
|
848
|
+
module.exports = __toCommonJS(db_connections_exports);
|
|
849
|
+
var import_get_context = require_get_context();
|
|
850
|
+
var DEBUG = !!process.env.DEBUG;
|
|
851
|
+
function getIdleTimeout(dbPool) {
|
|
852
|
+
if ("options" in dbPool && dbPool.options) {
|
|
853
|
+
if ("idleTimeoutMillis" in dbPool.options) {
|
|
854
|
+
return typeof dbPool.options.idleTimeoutMillis === "number" ? dbPool.options.idleTimeoutMillis : 1e4;
|
|
855
|
+
}
|
|
856
|
+
if ("maxIdleTimeMS" in dbPool.options) {
|
|
857
|
+
return typeof dbPool.options.maxIdleTimeMS === "number" ? dbPool.options.maxIdleTimeMS : 0;
|
|
858
|
+
}
|
|
859
|
+
if ("status" in dbPool) {
|
|
860
|
+
return 5e3;
|
|
861
|
+
}
|
|
862
|
+
if ("connect" in dbPool && "execute" in dbPool) {
|
|
863
|
+
return 3e4;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
if ("config" in dbPool && dbPool.config) {
|
|
867
|
+
if ("connectionConfig" in dbPool.config && dbPool.config.connectionConfig) {
|
|
868
|
+
return dbPool.config.connectionConfig.idleTimeout || 6e4;
|
|
869
|
+
}
|
|
870
|
+
if ("idleTimeout" in dbPool.config) {
|
|
871
|
+
return typeof dbPool.config.idleTimeout === "number" ? dbPool.config.idleTimeout : 6e4;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
if ("poolTimeout" in dbPool) {
|
|
875
|
+
return typeof dbPool.poolTimeout === "number" ? dbPool.poolTimeout : 6e4;
|
|
876
|
+
}
|
|
877
|
+
if ("idleTimeout" in dbPool) {
|
|
878
|
+
return typeof dbPool.idleTimeout === "number" ? dbPool.idleTimeout : 0;
|
|
879
|
+
}
|
|
880
|
+
return 1e4;
|
|
881
|
+
}
|
|
882
|
+
var idleTimeout = null;
|
|
883
|
+
var idleTimeoutResolve = () => {
|
|
884
|
+
};
|
|
885
|
+
var bootTime = Date.now();
|
|
886
|
+
var maximumDuration = 15 * 60 * 1e3 - 1e3;
|
|
887
|
+
function waitUntilIdleTimeout(dbPool) {
|
|
888
|
+
if (!process.env.VERCEL_URL || // This is not set during builds where we don't need to wait for idle connections using the mechanism
|
|
889
|
+
!process.env.VERCEL_REGION) {
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
if (idleTimeout) {
|
|
893
|
+
clearTimeout(idleTimeout);
|
|
894
|
+
idleTimeoutResolve();
|
|
895
|
+
}
|
|
896
|
+
const promise = new Promise((resolve) => {
|
|
897
|
+
idleTimeoutResolve = resolve;
|
|
898
|
+
});
|
|
899
|
+
const waitTime = Math.min(
|
|
900
|
+
getIdleTimeout(dbPool) + 100,
|
|
901
|
+
Math.max(100, maximumDuration - (Date.now() - bootTime))
|
|
902
|
+
);
|
|
903
|
+
idleTimeout = setTimeout(() => {
|
|
904
|
+
idleTimeoutResolve?.();
|
|
905
|
+
if (DEBUG) {
|
|
906
|
+
console.log("Database pool idle timeout reached. Releasing connections.");
|
|
907
|
+
}
|
|
908
|
+
}, waitTime);
|
|
909
|
+
const requestContext = (0, import_get_context.getContext)();
|
|
910
|
+
if (requestContext?.waitUntil) {
|
|
911
|
+
requestContext.waitUntil(promise);
|
|
912
|
+
} else {
|
|
913
|
+
console.warn("Pool release event triggered outside of request scope.");
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
function attachDatabasePool2(dbPool) {
|
|
917
|
+
if (idleTimeout) {
|
|
918
|
+
idleTimeoutResolve?.();
|
|
919
|
+
clearTimeout(idleTimeout);
|
|
920
|
+
}
|
|
921
|
+
if ("on" in dbPool && dbPool.on && "options" in dbPool && "idleTimeoutMillis" in dbPool.options) {
|
|
922
|
+
const pgPool = dbPool;
|
|
923
|
+
pgPool.on("release", () => {
|
|
924
|
+
if (DEBUG) {
|
|
925
|
+
console.log("Client released from pool");
|
|
926
|
+
}
|
|
927
|
+
waitUntilIdleTimeout(dbPool);
|
|
928
|
+
});
|
|
929
|
+
return;
|
|
930
|
+
} else if ("on" in dbPool && dbPool.on && "config" in dbPool && dbPool.config && "connectionConfig" in dbPool.config) {
|
|
931
|
+
const mysqlPool = dbPool;
|
|
932
|
+
mysqlPool.on("release", () => {
|
|
933
|
+
if (DEBUG) {
|
|
934
|
+
console.log("MySQL client released from pool");
|
|
935
|
+
}
|
|
936
|
+
waitUntilIdleTimeout(dbPool);
|
|
937
|
+
});
|
|
938
|
+
return;
|
|
939
|
+
} else if ("on" in dbPool && dbPool.on && "config" in dbPool && dbPool.config && "idleTimeout" in dbPool.config) {
|
|
940
|
+
const mysql2Pool = dbPool;
|
|
941
|
+
mysql2Pool.on("release", () => {
|
|
942
|
+
if (DEBUG) {
|
|
943
|
+
console.log("MySQL2/MariaDB client released from pool");
|
|
944
|
+
}
|
|
945
|
+
waitUntilIdleTimeout(dbPool);
|
|
946
|
+
});
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
if ("on" in dbPool && dbPool.on && "options" in dbPool && dbPool.options && "maxIdleTimeMS" in dbPool.options) {
|
|
950
|
+
const mongoPool = dbPool;
|
|
951
|
+
mongoPool.on("connectionCheckedOut", () => {
|
|
952
|
+
if (DEBUG) {
|
|
953
|
+
console.log("MongoDB connection checked out");
|
|
954
|
+
}
|
|
955
|
+
waitUntilIdleTimeout(dbPool);
|
|
956
|
+
});
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
if ("on" in dbPool && dbPool.on && "options" in dbPool && dbPool.options && "socket" in dbPool.options) {
|
|
960
|
+
const redisPool = dbPool;
|
|
961
|
+
redisPool.on("end", () => {
|
|
962
|
+
if (DEBUG) {
|
|
963
|
+
console.log("Redis connection ended");
|
|
964
|
+
}
|
|
965
|
+
waitUntilIdleTimeout(dbPool);
|
|
966
|
+
});
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
throw new Error("Unsupported database pool type");
|
|
970
|
+
}
|
|
971
|
+
var experimental_attachDatabasePool2 = attachDatabasePool2;
|
|
972
|
+
}
|
|
973
|
+
});
|
|
974
|
+
|
|
975
|
+
// ../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/purge/index.js
|
|
976
|
+
var require_purge = __commonJS({
|
|
977
|
+
"../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/purge/index.js"(exports, module) {
|
|
978
|
+
"use strict";
|
|
979
|
+
var __defProp2 = Object.defineProperty;
|
|
980
|
+
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
981
|
+
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
982
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
983
|
+
var __export = (target, all) => {
|
|
984
|
+
for (var name in all)
|
|
985
|
+
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
986
|
+
};
|
|
987
|
+
var __copyProps2 = (to, from, except, desc) => {
|
|
988
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
989
|
+
for (let key of __getOwnPropNames2(from))
|
|
990
|
+
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
991
|
+
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
992
|
+
}
|
|
993
|
+
return to;
|
|
994
|
+
};
|
|
995
|
+
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
996
|
+
var purge_exports = {};
|
|
997
|
+
__export(purge_exports, {
|
|
998
|
+
dangerouslyDeleteBySrcImage: () => dangerouslyDeleteBySrcImage2,
|
|
999
|
+
dangerouslyDeleteByTag: () => dangerouslyDeleteByTag2,
|
|
1000
|
+
invalidateBySrcImage: () => invalidateBySrcImage2,
|
|
1001
|
+
invalidateByTag: () => invalidateByTag2
|
|
1002
|
+
});
|
|
1003
|
+
module.exports = __toCommonJS(purge_exports);
|
|
1004
|
+
var import_get_context = require_get_context();
|
|
1005
|
+
var invalidateByTag2 = (tag) => {
|
|
1006
|
+
const api = (0, import_get_context.getContext)().purge;
|
|
1007
|
+
if (api) {
|
|
1008
|
+
return api.invalidateByTag(tag);
|
|
1009
|
+
}
|
|
1010
|
+
return Promise.resolve();
|
|
1011
|
+
};
|
|
1012
|
+
var dangerouslyDeleteByTag2 = (tag, options) => {
|
|
1013
|
+
const api = (0, import_get_context.getContext)().purge;
|
|
1014
|
+
if (api) {
|
|
1015
|
+
return api.dangerouslyDeleteByTag(tag, options);
|
|
1016
|
+
}
|
|
1017
|
+
return Promise.resolve();
|
|
1018
|
+
};
|
|
1019
|
+
var invalidateBySrcImage2 = (src) => {
|
|
1020
|
+
const api = (0, import_get_context.getContext)().purge;
|
|
1021
|
+
return api ? api.invalidateBySrcImage(src) : Promise.resolve();
|
|
1022
|
+
};
|
|
1023
|
+
var dangerouslyDeleteBySrcImage2 = (src, options) => {
|
|
1024
|
+
const api = (0, import_get_context.getContext)().purge;
|
|
1025
|
+
return api ? api.dangerouslyDeleteBySrcImage(src, options) : Promise.resolve();
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
// ../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/addcachetag/index.js
|
|
1031
|
+
var require_addcachetag = __commonJS({
|
|
1032
|
+
"../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/addcachetag/index.js"(exports, module) {
|
|
1033
|
+
"use strict";
|
|
1034
|
+
var __defProp2 = Object.defineProperty;
|
|
1035
|
+
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
1036
|
+
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
1037
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
1038
|
+
var __export = (target, all) => {
|
|
1039
|
+
for (var name in all)
|
|
1040
|
+
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
1041
|
+
};
|
|
1042
|
+
var __copyProps2 = (to, from, except, desc) => {
|
|
1043
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
1044
|
+
for (let key of __getOwnPropNames2(from))
|
|
1045
|
+
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
1046
|
+
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
1047
|
+
}
|
|
1048
|
+
return to;
|
|
1049
|
+
};
|
|
1050
|
+
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
1051
|
+
var addcachetag_exports = {};
|
|
1052
|
+
__export(addcachetag_exports, {
|
|
1053
|
+
addCacheTag: () => addCacheTag2
|
|
1054
|
+
});
|
|
1055
|
+
module.exports = __toCommonJS(addcachetag_exports);
|
|
1056
|
+
var import_get_context = require_get_context();
|
|
1057
|
+
var addCacheTag2 = (tag) => {
|
|
1058
|
+
const addCacheTag22 = (0, import_get_context.getContext)().addCacheTag;
|
|
1059
|
+
if (addCacheTag22) {
|
|
1060
|
+
return addCacheTag22(tag);
|
|
1061
|
+
}
|
|
1062
|
+
return Promise.resolve();
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
// ../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/index.js
|
|
1068
|
+
var require_functions = __commonJS({
|
|
1069
|
+
"../../node_modules/.pnpm/@vercel+functions@3.4.3_@aws-sdk+credential-provider-web-identity@3.972.20/node_modules/@vercel/functions/index.js"(exports, module) {
|
|
1070
|
+
"use strict";
|
|
1071
|
+
var __defProp2 = Object.defineProperty;
|
|
1072
|
+
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
1073
|
+
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
1074
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
1075
|
+
var __export = (target, all) => {
|
|
1076
|
+
for (var name in all)
|
|
1077
|
+
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
1078
|
+
};
|
|
1079
|
+
var __copyProps2 = (to, from, except, desc) => {
|
|
1080
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
1081
|
+
for (let key of __getOwnPropNames2(from))
|
|
1082
|
+
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
1083
|
+
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
1084
|
+
}
|
|
1085
|
+
return to;
|
|
1086
|
+
};
|
|
1087
|
+
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
1088
|
+
var src_exports = {};
|
|
1089
|
+
__export(src_exports, {
|
|
1090
|
+
addCacheTag: () => import_addcachetag.addCacheTag,
|
|
1091
|
+
attachDatabasePool: () => import_db_connections.attachDatabasePool,
|
|
1092
|
+
dangerouslyDeleteBySrcImage: () => import_purge.dangerouslyDeleteBySrcImage,
|
|
1093
|
+
dangerouslyDeleteByTag: () => import_purge.dangerouslyDeleteByTag,
|
|
1094
|
+
experimental_attachDatabasePool: () => import_db_connections.experimental_attachDatabasePool,
|
|
1095
|
+
geolocation: () => import_headers.geolocation,
|
|
1096
|
+
getCache: () => import_cache.getCache,
|
|
1097
|
+
getEnv: () => import_get_env.getEnv,
|
|
1098
|
+
invalidateBySrcImage: () => import_purge.invalidateBySrcImage,
|
|
1099
|
+
invalidateByTag: () => import_purge.invalidateByTag,
|
|
1100
|
+
ipAddress: () => import_headers.ipAddress,
|
|
1101
|
+
next: () => import_middleware.next,
|
|
1102
|
+
rewrite: () => import_middleware.rewrite,
|
|
1103
|
+
waitUntil: () => import_wait_until.waitUntil
|
|
1104
|
+
});
|
|
1105
|
+
module.exports = __toCommonJS(src_exports);
|
|
1106
|
+
var import_headers = require_headers();
|
|
1107
|
+
var import_get_env = require_get_env();
|
|
1108
|
+
var import_wait_until = require_wait_until();
|
|
1109
|
+
var import_middleware = require_middleware();
|
|
1110
|
+
var import_cache = require_cache();
|
|
1111
|
+
var import_db_connections = require_db_connections();
|
|
1112
|
+
var import_purge = require_purge();
|
|
1113
|
+
var import_addcachetag = require_addcachetag();
|
|
1114
|
+
}
|
|
1115
|
+
});
|
|
1116
|
+
|
|
1117
|
+
// src/next/auth.ts
|
|
1118
|
+
import { cache } from "react";
|
|
1119
|
+
|
|
1120
|
+
// src/fix-registry.ts
|
|
1121
|
+
var FIX_REGISTRY = {
|
|
1122
|
+
AUTH_TOKEN_INVALID: [
|
|
1123
|
+
"\u2192 Run `brokr env pull --stack <name>` to refresh your token",
|
|
1124
|
+
"\u2192 Or run `brokr link account` to re-authenticate"
|
|
1125
|
+
].join("\n"),
|
|
1126
|
+
AUTH_SESSION_EXPIRED: [
|
|
1127
|
+
"\u2192 Run `brokr link account` to re-authenticate"
|
|
1128
|
+
].join("\n"),
|
|
1129
|
+
BROKR_TOKEN_MISSING: [
|
|
1130
|
+
"\u2192 Run `brokr env pull --stack <name>` to sync your environment",
|
|
1131
|
+
"\u2192 Or add BROKR_TOKEN to your .env.local manually"
|
|
1132
|
+
].join("\n"),
|
|
1133
|
+
DATABASE_PROVISION_FAILED: [
|
|
1134
|
+
"\u2192 Run `brokr status <stack>` to check current state",
|
|
1135
|
+
"\u2192 Run `brokr retry <stack>` to re-trigger provisioning"
|
|
1136
|
+
].join("\n"),
|
|
1137
|
+
DATABASE_QUOTA_REACHED: [
|
|
1138
|
+
"\u2192 Upgrade your plan or delete unused databases",
|
|
1139
|
+
"\u2192 Check usage at brokr.sh/billing"
|
|
1140
|
+
].join("\n"),
|
|
1141
|
+
DATABASE_HEALTH_TIMEOUT: [
|
|
1142
|
+
"\u2192 Run `brokr status <stack>` to check database state",
|
|
1143
|
+
"\u2192 If persistent, check provider status page"
|
|
1144
|
+
].join("\n"),
|
|
1145
|
+
DEPLOYMENT_FAILED: [
|
|
1146
|
+
"\u2192 Run `brokr logs --stack <name>` to see build logs",
|
|
1147
|
+
"\u2192 Fix the build error and run `brokr deploy`"
|
|
1148
|
+
].join("\n"),
|
|
1149
|
+
DEPLOYMENT_RATE_LIMITED: [
|
|
1150
|
+
"\u2192 Wait a minute and retry",
|
|
1151
|
+
"\u2192 Vercel rate limits apply per project"
|
|
1152
|
+
].join("\n"),
|
|
1153
|
+
REPO_ALREADY_EXISTS: [
|
|
1154
|
+
"\u2192 Use a different stack name",
|
|
1155
|
+
"\u2192 Or run `brokr status <stack>` to check the existing stack"
|
|
1156
|
+
].join("\n"),
|
|
1157
|
+
REPO_CREATE_FAILED: [
|
|
1158
|
+
"\u2192 Check your GitHub connection: `brokr link account`",
|
|
1159
|
+
"\u2192 Ensure the Brokr GitHub App is installed"
|
|
1160
|
+
].join("\n"),
|
|
1161
|
+
EMAIL_DOMAIN_FAILED: [
|
|
1162
|
+
"\u2192 Check DNS records are propagated",
|
|
1163
|
+
"\u2192 Run `brokr status <stack>` for details"
|
|
1164
|
+
].join("\n"),
|
|
1165
|
+
DNS_RECORD_FAILED: [
|
|
1166
|
+
"\u2192 Check Cloudflare dashboard for zone status",
|
|
1167
|
+
"\u2192 Retry with `brokr retry <stack>`"
|
|
1168
|
+
].join("\n"),
|
|
1169
|
+
INSUFFICIENT_CREDITS: [
|
|
1170
|
+
"\u2192 Top up credits at brokr.sh/billing",
|
|
1171
|
+
"\u2192 Or run `brokr billing topup`"
|
|
1172
|
+
].join("\n"),
|
|
1173
|
+
AI_BUDGET_EXCEEDED: [
|
|
1174
|
+
"\u2192 Top up credits at brokr.sh/billing",
|
|
1175
|
+
"\u2192 Or run `brokr billing topup`"
|
|
1176
|
+
].join("\n"),
|
|
1177
|
+
AI_PROVIDER_UNAVAILABLE: [
|
|
1178
|
+
"\u2192 The AI provider is temporarily down",
|
|
1179
|
+
"\u2192 Retry in a few seconds \u2014 this is usually transient"
|
|
1180
|
+
].join("\n"),
|
|
1181
|
+
AI_MODEL_NOT_FOUND: [
|
|
1182
|
+
"\u2192 Check the model name in your configuration",
|
|
1183
|
+
"\u2192 See available models at brokr.sh/docs/ai-models"
|
|
1184
|
+
].join("\n"),
|
|
1185
|
+
ENV_VAR_MISSING: [
|
|
1186
|
+
"\u2192 Run `brokr env pull --stack <name>` to sync environment",
|
|
1187
|
+
"\u2192 Check required vars in your template README"
|
|
1188
|
+
].join("\n"),
|
|
1189
|
+
STACK_LIMIT_REACHED: [
|
|
1190
|
+
"\u2192 Delete unused stacks with `brokr delete <stack>`",
|
|
1191
|
+
"\u2192 Or upgrade your plan at brokr.sh/billing"
|
|
1192
|
+
].join("\n"),
|
|
1193
|
+
STACK_NOT_FOUND: [
|
|
1194
|
+
"\u2192 Check the stack name: `brokr list`",
|
|
1195
|
+
"\u2192 Create a new stack: `brokr create --name <name>`"
|
|
1196
|
+
].join("\n"),
|
|
1197
|
+
RATE_LIMITED: [
|
|
1198
|
+
"\u2192 Wait a moment and retry",
|
|
1199
|
+
"\u2192 If persistent, check brokr.sh/status"
|
|
1200
|
+
].join("\n"),
|
|
1201
|
+
GATEWAY_AUTH_FAILED: [
|
|
1202
|
+
"\u2192 Your BROKR_TOKEN may be expired",
|
|
1203
|
+
"\u2192 Run `brokr env pull --stack <name>` to refresh"
|
|
1204
|
+
].join("\n"),
|
|
1205
|
+
BROKR_TOKEN_INVALID: [
|
|
1206
|
+
"\u2192 Your BROKR_TOKEN has expired or been revoked",
|
|
1207
|
+
"\u2192 Run `brokr env pull --stack <name>` to get a fresh token"
|
|
1208
|
+
].join("\n"),
|
|
1209
|
+
AI_STREAM_ERROR: [
|
|
1210
|
+
"\u2192 The AI stream was interrupted",
|
|
1211
|
+
"\u2192 Retry the request \u2014 this is usually transient"
|
|
1212
|
+
].join("\n"),
|
|
1213
|
+
EMAIL_SEND_FAILED: [
|
|
1214
|
+
"\u2192 Email delivery failed",
|
|
1215
|
+
"\u2192 Check that your domain DNS is verified: `brokr status <stack>`"
|
|
1216
|
+
].join("\n"),
|
|
1217
|
+
EMAIL_NOT_CONFIGURED: [
|
|
1218
|
+
"\u2192 Email is not set up for this stack",
|
|
1219
|
+
"\u2192 Add email capability: `brokr add email --stack <name>`"
|
|
1220
|
+
].join("\n"),
|
|
1221
|
+
EMAIL_INVALID_FROM: [
|
|
1222
|
+
"\u2192 The from address must match your verified domain",
|
|
1223
|
+
"\u2192 Check your domain: `brokr status <stack>`"
|
|
1224
|
+
].join("\n"),
|
|
1225
|
+
STORAGE_UPLOAD_FAILED: [
|
|
1226
|
+
"\u2192 File upload failed",
|
|
1227
|
+
"\u2192 Check file size limits and retry"
|
|
1228
|
+
].join("\n"),
|
|
1229
|
+
STORAGE_NOT_FOUND: [
|
|
1230
|
+
"\u2192 The requested file does not exist",
|
|
1231
|
+
"\u2192 Check the key and try again"
|
|
1232
|
+
].join("\n"),
|
|
1233
|
+
PAYMENTS_NOT_CONFIGURED: [
|
|
1234
|
+
"\u2192 Payments are not set up for this stack",
|
|
1235
|
+
"\u2192 Run `brokr payments sync` to configure Stripe"
|
|
1236
|
+
].join("\n"),
|
|
1237
|
+
PAYMENTS_FAILED: [
|
|
1238
|
+
"\u2192 Payment processing failed",
|
|
1239
|
+
"\u2192 Check your Stripe dashboard for details"
|
|
1240
|
+
].join("\n"),
|
|
1241
|
+
GATEWAY_INTERNAL: [
|
|
1242
|
+
"\u2192 The Brokr gateway encountered an internal error",
|
|
1243
|
+
"\u2192 Check brokr.sh/status for service health"
|
|
1244
|
+
].join("\n"),
|
|
1245
|
+
UPSTREAM_ERROR: [
|
|
1246
|
+
"\u2192 An upstream service is temporarily unavailable",
|
|
1247
|
+
"\u2192 Retry in a few seconds \u2014 this is usually transient"
|
|
1248
|
+
].join("\n"),
|
|
1249
|
+
NETWORK_ERROR: [
|
|
1250
|
+
"\u2192 Could not reach the Brokr gateway",
|
|
1251
|
+
"\u2192 Check your internet connection",
|
|
1252
|
+
"\u2192 Check brokr.sh/status for service health"
|
|
1253
|
+
].join("\n"),
|
|
1254
|
+
TIMEOUT: [
|
|
1255
|
+
"\u2192 Request timed out",
|
|
1256
|
+
"\u2192 Retry \u2014 if persistent, check brokr.sh/status"
|
|
1257
|
+
].join("\n")
|
|
1258
|
+
};
|
|
1259
|
+
|
|
1260
|
+
// src/errors.ts
|
|
1261
|
+
var BrokrError = class extends Error {
|
|
1262
|
+
constructor(message, code, capability, retryable = false, errorCode, requestId, hint) {
|
|
1263
|
+
super(message);
|
|
1264
|
+
this.code = code;
|
|
1265
|
+
this.capability = capability;
|
|
1266
|
+
this.retryable = retryable;
|
|
1267
|
+
this.errorCode = errorCode;
|
|
1268
|
+
this.requestId = requestId;
|
|
1269
|
+
this.hint = hint;
|
|
1270
|
+
this.name = "BrokrError";
|
|
1271
|
+
}
|
|
1272
|
+
/** Multi-line terminal fix block — looked up from error code. */
|
|
1273
|
+
get fix() {
|
|
1274
|
+
if (!this.errorCode) return void 0;
|
|
1275
|
+
return FIX_REGISTRY[this.errorCode];
|
|
1276
|
+
}
|
|
1277
|
+
/** Documentation URL for this error code. */
|
|
1278
|
+
get docsUrl() {
|
|
1279
|
+
if (!this.errorCode) return void 0;
|
|
1280
|
+
return `https://brokr.sh/errors/${this.errorCode.toLowerCase().replace(/_/g, "-")}`;
|
|
1281
|
+
}
|
|
1282
|
+
/** Safe message for end users — zero technical language. */
|
|
1283
|
+
toUserMessage() {
|
|
1284
|
+
switch (this.code) {
|
|
1285
|
+
case "RATE_LIMITED":
|
|
1286
|
+
return "Please wait a moment and try again.";
|
|
1287
|
+
case "TIMEOUT":
|
|
1288
|
+
return "The request took too long. Please try again.";
|
|
1289
|
+
case "NETWORK_ERROR":
|
|
1290
|
+
return "Connection issue. Check your internet and try again.";
|
|
1291
|
+
case "BROKR_TOKEN_MISSING":
|
|
1292
|
+
return "App is not connected to Brokr. Contact the developer.";
|
|
1293
|
+
case "BROKR_TOKEN_INVALID":
|
|
1294
|
+
return "Session expired. Please refresh.";
|
|
1295
|
+
case "AI_BUDGET_EXCEEDED":
|
|
1296
|
+
return "AI usage limit reached. The developer needs to add credits.";
|
|
1297
|
+
case "AI_PROVIDER_UNAVAILABLE":
|
|
1298
|
+
return "AI service is temporarily down. Please retry shortly.";
|
|
1299
|
+
case "AI_PROVIDER_RATE_LIMITED":
|
|
1300
|
+
return "Too many AI requests. Please wait a moment.";
|
|
1301
|
+
case "INSUFFICIENT_CREDITS":
|
|
1302
|
+
return "Not enough credits. Please top up your balance.";
|
|
1303
|
+
case "STORAGE_UPLOAD_FAILED":
|
|
1304
|
+
return "File upload failed. Please try again.";
|
|
1305
|
+
case "STORAGE_NOT_FOUND":
|
|
1306
|
+
return "File not found.";
|
|
1307
|
+
case "EMAIL_SEND_FAILED":
|
|
1308
|
+
return "Email could not be sent. Please try again.";
|
|
1309
|
+
case "EMAIL_NOT_CONFIGURED":
|
|
1310
|
+
return "Email is not set up for this app.";
|
|
1311
|
+
case "PAYMENTS_NOT_CONFIGURED":
|
|
1312
|
+
return "Payments are not configured. Contact the developer.";
|
|
1313
|
+
case "PAYMENTS_FAILED":
|
|
1314
|
+
return "Payment processing failed. Please try again.";
|
|
1315
|
+
case "NOT_FOUND":
|
|
1316
|
+
return "The requested resource was not found.";
|
|
1317
|
+
case "VALIDATION_ERROR":
|
|
1318
|
+
return "Invalid input. Please check your data and try again.";
|
|
1319
|
+
default:
|
|
1320
|
+
return "Something went wrong. We're looking into it.";
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
toString() {
|
|
1324
|
+
return `${this.name} [${this.errorCode ?? this.code}]: ${this.message}`;
|
|
1325
|
+
}
|
|
1326
|
+
toJSON() {
|
|
1327
|
+
return {
|
|
1328
|
+
name: this.name,
|
|
1329
|
+
code: this.code,
|
|
1330
|
+
errorCode: this.errorCode,
|
|
1331
|
+
message: this.message,
|
|
1332
|
+
capability: this.capability,
|
|
1333
|
+
retryable: this.retryable,
|
|
1334
|
+
requestId: this.requestId,
|
|
1335
|
+
hint: this.hint,
|
|
1336
|
+
component: this.component
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
};
|
|
1340
|
+
var BrokrAuthError = class extends BrokrError {
|
|
1341
|
+
constructor(message, code) {
|
|
1342
|
+
super(message, code, "auth", false);
|
|
1343
|
+
this.name = "BrokrAuthError";
|
|
1344
|
+
}
|
|
1345
|
+
};
|
|
1346
|
+
var BrokrRateLimitError = class extends BrokrError {
|
|
1347
|
+
constructor(message, retryAfter, capability) {
|
|
1348
|
+
super(message, "RATE_LIMITED", capability, true);
|
|
1349
|
+
this.retryAfter = retryAfter;
|
|
1350
|
+
this.name = "BrokrRateLimitError";
|
|
1351
|
+
}
|
|
1352
|
+
};
|
|
1353
|
+
var BrokrNetworkError = class extends BrokrError {
|
|
1354
|
+
constructor(message, capability) {
|
|
1355
|
+
super(message, "NETWORK_ERROR", capability, true);
|
|
1356
|
+
this.name = "BrokrNetworkError";
|
|
1357
|
+
}
|
|
1358
|
+
};
|
|
1359
|
+
var BrokrTimeoutError = class extends BrokrError {
|
|
1360
|
+
constructor(message, capability) {
|
|
1361
|
+
super(message, "TIMEOUT", capability, true);
|
|
1362
|
+
this.name = "BrokrTimeoutError";
|
|
1363
|
+
}
|
|
1364
|
+
};
|
|
1365
|
+
|
|
1366
|
+
// src/next/auth.ts
|
|
1367
|
+
function resolveAppUrl() {
|
|
1368
|
+
const localUrl = process.env.BETTER_AUTH_URL ?? process.env.NEXTAUTH_URL ?? process.env.NEXT_PUBLIC_APP_URL ?? (process.env.NODE_ENV !== "production" ? "http://localhost:3000" : void 0);
|
|
1369
|
+
const url = localUrl ?? process.env.APP_URL ?? process.env.BROKR_AUTH_URL;
|
|
1370
|
+
if (url) return url;
|
|
1371
|
+
const vercelUrl = process.env.VERCEL_URL;
|
|
1372
|
+
if (vercelUrl) return `https://${vercelUrl}`;
|
|
1373
|
+
if (process.env.NODE_ENV === "production") {
|
|
1374
|
+
throw new BrokrError(
|
|
1375
|
+
"[brokr] Cannot resolve auth URL in production. Set BETTER_AUTH_URL or APP_URL in your environment.",
|
|
1376
|
+
"AUTH_NOT_CONFIGURED",
|
|
1377
|
+
"auth"
|
|
1378
|
+
);
|
|
1379
|
+
}
|
|
1380
|
+
return "http://localhost:3000";
|
|
1381
|
+
}
|
|
1382
|
+
var getSessionFromCookies = cache(async function getSessionFromCookiesCached() {
|
|
1383
|
+
const { cookies } = await import("next/headers");
|
|
1384
|
+
const cookieStore = await cookies();
|
|
1385
|
+
const cookieHeader = cookieStore.getAll().map((c) => `${c.name}=${c.value}`).join("; ");
|
|
1386
|
+
if (!cookieHeader) return null;
|
|
1387
|
+
const appUrl = resolveAppUrl();
|
|
1388
|
+
const res = await fetch(`${appUrl}/api/auth/get-session`, {
|
|
1389
|
+
method: "GET",
|
|
1390
|
+
headers: { cookie: cookieHeader },
|
|
1391
|
+
cache: "no-store"
|
|
1392
|
+
// Don't cache across different users
|
|
1393
|
+
});
|
|
1394
|
+
if (!res.ok) return null;
|
|
1395
|
+
const data = await res.json();
|
|
1396
|
+
if (!data?.user || !data?.session) return null;
|
|
1397
|
+
return {
|
|
1398
|
+
user: {
|
|
1399
|
+
id: data.user.id,
|
|
1400
|
+
email: data.user.email,
|
|
1401
|
+
name: data.user.name ?? null,
|
|
1402
|
+
image: data.user.image ?? null,
|
|
1403
|
+
emailVerified: data.user.emailVerified ? /* @__PURE__ */ new Date() : null
|
|
1404
|
+
},
|
|
1405
|
+
sessionId: data.session.id,
|
|
1406
|
+
expiresAt: new Date(data.session.expiresAt)
|
|
1407
|
+
};
|
|
1408
|
+
});
|
|
1409
|
+
async function protectApi() {
|
|
1410
|
+
const session = await getSessionFromCookies();
|
|
1411
|
+
if (!session) {
|
|
1412
|
+
throw new BrokrError("[brokr] Authentication required.", "UNAUTHORIZED", "auth");
|
|
1413
|
+
}
|
|
1414
|
+
return session.user;
|
|
1415
|
+
}
|
|
1416
|
+
async function protectPage(redirectTo = "/sign-in") {
|
|
1417
|
+
const session = await getSessionFromCookies();
|
|
1418
|
+
if (!session) {
|
|
1419
|
+
const { redirect } = await import("next/navigation");
|
|
1420
|
+
redirect(redirectTo);
|
|
1421
|
+
throw new Error("unreachable");
|
|
1422
|
+
}
|
|
1423
|
+
return session.user;
|
|
1424
|
+
}
|
|
1425
|
+
function withUser(fn) {
|
|
1426
|
+
return async (request) => {
|
|
1427
|
+
const user = await protectApi();
|
|
1428
|
+
return fn(request, user);
|
|
1429
|
+
};
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
// src/gateway.ts
|
|
1433
|
+
var GATEWAY_URL = "https://api.brokr.sh";
|
|
1434
|
+
var FETCH_TIMEOUT_MS = 3e4;
|
|
1435
|
+
function requireToken(token, capability) {
|
|
1436
|
+
if (!token) {
|
|
1437
|
+
let hint = "brokr env pull --stack <name>";
|
|
1438
|
+
try {
|
|
1439
|
+
if (typeof process !== "undefined") {
|
|
1440
|
+
const fs = __require("fs");
|
|
1441
|
+
const path = __require("path");
|
|
1442
|
+
const brokrFile = path.join(process.cwd(), ".brokr");
|
|
1443
|
+
if (fs.existsSync(brokrFile)) {
|
|
1444
|
+
const data = JSON.parse(fs.readFileSync(brokrFile, "utf8"));
|
|
1445
|
+
if (data?.stackName) hint = `brokr env pull --stack ${data.stackName}`;
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
} catch {
|
|
1449
|
+
}
|
|
1450
|
+
throw new BrokrAuthError(
|
|
1451
|
+
`BROKR_TOKEN is not set. Run: ${hint}`,
|
|
1452
|
+
"BROKR_TOKEN_MISSING"
|
|
1453
|
+
);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
async function gatewayFetch(gatewayUrl, token, path, body, capability) {
|
|
1457
|
+
const controller = new AbortController();
|
|
1458
|
+
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
1459
|
+
let res;
|
|
1460
|
+
try {
|
|
1461
|
+
res = await fetch(`${gatewayUrl}${path}`, {
|
|
1462
|
+
method: "POST",
|
|
1463
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
1464
|
+
body: JSON.stringify(body),
|
|
1465
|
+
signal: controller.signal
|
|
1466
|
+
});
|
|
1467
|
+
} catch (err) {
|
|
1468
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
1469
|
+
throw new BrokrTimeoutError(
|
|
1470
|
+
`Request timed out after ${FETCH_TIMEOUT_MS / 1e3}s`,
|
|
1471
|
+
capability
|
|
1472
|
+
);
|
|
1473
|
+
}
|
|
1474
|
+
throw new BrokrNetworkError(
|
|
1475
|
+
"Could not reach Brokr gateway. Check your network.",
|
|
1476
|
+
capability
|
|
1477
|
+
);
|
|
1478
|
+
} finally {
|
|
1479
|
+
clearTimeout(timeout);
|
|
1480
|
+
}
|
|
1481
|
+
if (res.status === 429) {
|
|
1482
|
+
const rawRetryAfter = parseInt(res.headers.get("Retry-After") ?? "60", 10);
|
|
1483
|
+
const retryAfter = Number.isFinite(rawRetryAfter) ? rawRetryAfter : 60;
|
|
1484
|
+
const data = await res.json().catch(() => ({}));
|
|
1485
|
+
const errorMsg = typeof data.error === "string" ? data.error : `Rate limited (retry after ${retryAfter}s)`;
|
|
1486
|
+
throw new BrokrRateLimitError(
|
|
1487
|
+
errorMsg,
|
|
1488
|
+
retryAfter,
|
|
1489
|
+
capability
|
|
1490
|
+
);
|
|
1491
|
+
}
|
|
1492
|
+
if (res.status === 401) {
|
|
1493
|
+
throw new BrokrAuthError("Invalid or expired BROKR_TOKEN.", "BROKR_TOKEN_INVALID");
|
|
1494
|
+
}
|
|
1495
|
+
if (!res.ok) {
|
|
1496
|
+
const body2 = await res.json().catch(() => ({}));
|
|
1497
|
+
const errObj = typeof body2.error === "object" && body2.error !== null ? body2.error : void 0;
|
|
1498
|
+
const errorData = body2.data ?? errObj?.data ?? body2;
|
|
1499
|
+
const errorCode = errorData.errorCode ?? body2.code;
|
|
1500
|
+
const hint = errorData.hint;
|
|
1501
|
+
const requestId = errorData.requestId ?? res.headers.get("x-request-id");
|
|
1502
|
+
const retryable = errorData.retryable;
|
|
1503
|
+
const errorStr = typeof body2.error === "string" ? body2.error : void 0;
|
|
1504
|
+
const message = body2.message ?? errObj?.message ?? errorStr ?? `${capability} request failed (HTTP ${res.status})`;
|
|
1505
|
+
throw new BrokrError(
|
|
1506
|
+
message,
|
|
1507
|
+
errorCode ?? `${capability.toUpperCase()}_FAILED`,
|
|
1508
|
+
capability,
|
|
1509
|
+
retryable ?? false,
|
|
1510
|
+
errorCode ?? void 0,
|
|
1511
|
+
requestId ?? void 0,
|
|
1512
|
+
hint ?? void 0
|
|
1513
|
+
);
|
|
1514
|
+
}
|
|
1515
|
+
try {
|
|
1516
|
+
return await res.json();
|
|
1517
|
+
} catch {
|
|
1518
|
+
throw new BrokrError(
|
|
1519
|
+
`${capability} returned invalid response (expected JSON).`,
|
|
1520
|
+
`${capability.toUpperCase()}_INVALID_RESPONSE`,
|
|
1521
|
+
capability,
|
|
1522
|
+
true
|
|
1523
|
+
);
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
async function gatewayStream(gatewayUrl, token, path, body, capability) {
|
|
1527
|
+
const controller = new AbortController();
|
|
1528
|
+
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
1529
|
+
let res;
|
|
1530
|
+
try {
|
|
1531
|
+
res = await fetch(`${gatewayUrl}${path}`, {
|
|
1532
|
+
method: "POST",
|
|
1533
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
1534
|
+
body: JSON.stringify(body),
|
|
1535
|
+
signal: controller.signal
|
|
1536
|
+
});
|
|
1537
|
+
} catch (err) {
|
|
1538
|
+
clearTimeout(timeout);
|
|
1539
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
1540
|
+
throw new BrokrTimeoutError(
|
|
1541
|
+
`Stream request timed out after ${FETCH_TIMEOUT_MS / 1e3}s`,
|
|
1542
|
+
capability
|
|
1543
|
+
);
|
|
1544
|
+
}
|
|
1545
|
+
throw new BrokrNetworkError(
|
|
1546
|
+
"Could not reach Brokr gateway. Check your network.",
|
|
1547
|
+
capability
|
|
1548
|
+
);
|
|
1549
|
+
}
|
|
1550
|
+
clearTimeout(timeout);
|
|
1551
|
+
if (res.status === 429) {
|
|
1552
|
+
const retryAfter = parseInt(res.headers.get("Retry-After") ?? "60", 10);
|
|
1553
|
+
throw new BrokrRateLimitError("Rate limited.", retryAfter, capability);
|
|
1554
|
+
}
|
|
1555
|
+
if (res.status === 401) {
|
|
1556
|
+
throw new BrokrAuthError("Invalid or expired BROKR_TOKEN.", "BROKR_TOKEN_INVALID");
|
|
1557
|
+
}
|
|
1558
|
+
if (!res.ok || !res.body) {
|
|
1559
|
+
throw new BrokrError(
|
|
1560
|
+
`${capability} stream failed (HTTP ${res.status})`,
|
|
1561
|
+
`${capability.toUpperCase()}_STREAM_FAILED`,
|
|
1562
|
+
capability
|
|
1563
|
+
);
|
|
1564
|
+
}
|
|
1565
|
+
return res;
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
// src/logs/capture.ts
|
|
1569
|
+
var DEFAULT_BATCH_SIZE = 20;
|
|
1570
|
+
var DEFAULT_FLUSH_INTERVAL_MS = 5e3;
|
|
1571
|
+
var buffer = [];
|
|
1572
|
+
var flushTimer = null;
|
|
1573
|
+
var config = null;
|
|
1574
|
+
var intercepted = false;
|
|
1575
|
+
function initCapture(opts = {}) {
|
|
1576
|
+
if (config) return;
|
|
1577
|
+
const token = opts.token ?? (typeof process !== "undefined" ? process.env.BROKR_TOKEN : void 0);
|
|
1578
|
+
const stackId = opts.stackId ?? resolveStackId();
|
|
1579
|
+
if (!token || !stackId) {
|
|
1580
|
+
if (typeof process !== "undefined") {
|
|
1581
|
+
console.log(`[brokr] Log capture skipped: token=${token ? "present" : "MISSING"} stackId=${stackId ?? "MISSING"}`);
|
|
1582
|
+
}
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1585
|
+
const apiUrl = opts.apiUrl ?? opts.gatewayUrl ?? (typeof process !== "undefined" ? process.env.BROKR_GATEWAY_URL : void 0) ?? "https://api.brokr.sh";
|
|
1586
|
+
config = {
|
|
1587
|
+
token,
|
|
1588
|
+
stackId,
|
|
1589
|
+
apiUrl: apiUrl.replace(/\/+$/, ""),
|
|
1590
|
+
batchSize: opts.batchSize ?? DEFAULT_BATCH_SIZE,
|
|
1591
|
+
flushIntervalMs: opts.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS
|
|
1592
|
+
};
|
|
1593
|
+
if (typeof process !== "undefined") {
|
|
1594
|
+
console.log(`[brokr] Log capture initialized \u2192 ${apiUrl}/v1/logs/ingest (stack: ${stackId.slice(0, 8)}...)`);
|
|
1595
|
+
}
|
|
1596
|
+
if (flushTimer) clearInterval(flushTimer);
|
|
1597
|
+
flushTimer = setInterval(() => flush(), config.flushIntervalMs);
|
|
1598
|
+
if (typeof process !== "undefined" && process.on) {
|
|
1599
|
+
process.on("beforeExit", () => flush());
|
|
1600
|
+
}
|
|
1601
|
+
if (typeof process !== "undefined" && !intercepted) {
|
|
1602
|
+
intercepted = true;
|
|
1603
|
+
const origError = console.error;
|
|
1604
|
+
const origWarn = console.warn;
|
|
1605
|
+
console.error = (...args) => {
|
|
1606
|
+
origError.apply(console, args);
|
|
1607
|
+
try {
|
|
1608
|
+
const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
|
|
1609
|
+
if (!msg.startsWith("[brokr]")) capture("error", msg, "console.error");
|
|
1610
|
+
} catch {
|
|
1611
|
+
}
|
|
1612
|
+
};
|
|
1613
|
+
console.warn = (...args) => {
|
|
1614
|
+
origWarn.apply(console, args);
|
|
1615
|
+
try {
|
|
1616
|
+
const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
|
|
1617
|
+
if (!msg.startsWith("[brokr]")) capture("warn", msg, "console.warn");
|
|
1618
|
+
} catch {
|
|
1619
|
+
}
|
|
1620
|
+
};
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
function capture(level, message, source, stackTrace) {
|
|
1624
|
+
if (!config) return;
|
|
1625
|
+
buffer.push({
|
|
1626
|
+
level,
|
|
1627
|
+
message: message.slice(0, 1e4),
|
|
1628
|
+
source: source ?? "app",
|
|
1629
|
+
stackTrace: stackTrace?.slice(0, 5e4),
|
|
1630
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1631
|
+
});
|
|
1632
|
+
if (buffer.length >= (config.batchSize ?? DEFAULT_BATCH_SIZE)) {
|
|
1633
|
+
flush();
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
function captureRequest(method, path, statusCode, durationMs) {
|
|
1637
|
+
const level = statusCode >= 500 ? "error" : statusCode >= 400 ? "warn" : "info";
|
|
1638
|
+
const dur = durationMs != null ? ` ${durationMs}ms` : "";
|
|
1639
|
+
capture(level, `${method} ${path} ${statusCode}${dur}`, path);
|
|
1640
|
+
}
|
|
1641
|
+
function flush() {
|
|
1642
|
+
if (!config || buffer.length === 0) return;
|
|
1643
|
+
const entries = buffer.splice(0);
|
|
1644
|
+
const { token, stackId, apiUrl } = config;
|
|
1645
|
+
fetch(`${apiUrl}/v1/logs/ingest`, {
|
|
1646
|
+
method: "POST",
|
|
1647
|
+
headers: {
|
|
1648
|
+
"Content-Type": "application/json",
|
|
1649
|
+
Authorization: `Bearer ${token}`
|
|
1650
|
+
},
|
|
1651
|
+
body: JSON.stringify({ stackId, entries })
|
|
1652
|
+
}).catch((err) => {
|
|
1653
|
+
if (typeof console !== "undefined") {
|
|
1654
|
+
console.warn("[brokr] Log flush failed:", err instanceof Error ? err.message : err);
|
|
1655
|
+
}
|
|
1656
|
+
});
|
|
1657
|
+
}
|
|
1658
|
+
function resolveStackId() {
|
|
1659
|
+
if (typeof process === "undefined") return void 0;
|
|
1660
|
+
if (process.env.BROKR_STACK_ID) return process.env.BROKR_STACK_ID;
|
|
1661
|
+
try {
|
|
1662
|
+
const fs = __require("fs");
|
|
1663
|
+
const path = __require("path");
|
|
1664
|
+
const brokrFile = path.join(process.cwd(), ".brokr");
|
|
1665
|
+
if (fs.existsSync(brokrFile)) {
|
|
1666
|
+
const content = fs.readFileSync(brokrFile, "utf8");
|
|
1667
|
+
const match = content.match(/BROKR_STACK_ID=(.+)/);
|
|
1668
|
+
if (match) return match[1].trim();
|
|
1669
|
+
}
|
|
1670
|
+
} catch {
|
|
1671
|
+
}
|
|
1672
|
+
return void 0;
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
// src/ai/client.ts
|
|
1676
|
+
function normalizeInput(input) {
|
|
1677
|
+
if (typeof input === "string") {
|
|
1678
|
+
return [{ role: "user", content: input }];
|
|
1679
|
+
}
|
|
1680
|
+
return input;
|
|
1681
|
+
}
|
|
1682
|
+
var BrokrAIClient = class {
|
|
1683
|
+
constructor(_token, _gatewayUrl) {
|
|
1684
|
+
this._token = _token;
|
|
1685
|
+
this._gatewayUrl = _gatewayUrl;
|
|
1686
|
+
}
|
|
1687
|
+
// ---------------------------------------------------------------------------
|
|
1688
|
+
// Core chat
|
|
1689
|
+
// ---------------------------------------------------------------------------
|
|
1690
|
+
/**
|
|
1691
|
+
* Send a chat completion request.
|
|
1692
|
+
* Accepts a string (auto-wrapped as user message) or a message array.
|
|
1693
|
+
*/
|
|
1694
|
+
async chat(input, options) {
|
|
1695
|
+
requireToken(this._token, "ai");
|
|
1696
|
+
const messages = normalizeInput(input);
|
|
1697
|
+
try {
|
|
1698
|
+
const data = await gatewayFetch(this._gatewayUrl, this._token, "/v1/chat/completions", {
|
|
1699
|
+
messages,
|
|
1700
|
+
model: options?.model,
|
|
1701
|
+
max_tokens: options?.maxTokens,
|
|
1702
|
+
temperature: options?.temperature
|
|
1703
|
+
}, "ai");
|
|
1704
|
+
return {
|
|
1705
|
+
content: data.choices?.[0]?.message?.content ?? "",
|
|
1706
|
+
model: data.model ?? "",
|
|
1707
|
+
usage: {
|
|
1708
|
+
promptTokens: data.usage?.prompt_tokens ?? 0,
|
|
1709
|
+
completionTokens: data.usage?.completion_tokens ?? 0
|
|
1710
|
+
}
|
|
1711
|
+
};
|
|
1712
|
+
} catch (err) {
|
|
1713
|
+
if (err instanceof BrokrError) err.component = "AIChat";
|
|
1714
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1715
|
+
capture("error", `AI chat failed: ${msg}`, "brokr.ai.chat", err instanceof Error ? err.stack : void 0);
|
|
1716
|
+
throw err;
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
/**
|
|
1720
|
+
* Stream a chat completion. Yields text strings directly.
|
|
1721
|
+
* Accepts a string (auto-wrapped as user message) or a message array.
|
|
1722
|
+
*/
|
|
1723
|
+
/**
|
|
1724
|
+
* Stream a chat completion. Yields text strings directly.
|
|
1725
|
+
* Tries streaming first — if the gateway or provider doesn't support it,
|
|
1726
|
+
* falls back to a non-streaming call and yields the full response at once.
|
|
1727
|
+
*/
|
|
1728
|
+
async *stream(input, options) {
|
|
1729
|
+
requireToken(this._token, "ai");
|
|
1730
|
+
const messages = normalizeInput(input);
|
|
1731
|
+
let res;
|
|
1732
|
+
try {
|
|
1733
|
+
res = await gatewayStream(
|
|
1734
|
+
this._gatewayUrl,
|
|
1735
|
+
this._token,
|
|
1736
|
+
"/v1/chat/completions",
|
|
1737
|
+
{
|
|
1738
|
+
messages,
|
|
1739
|
+
stream: true,
|
|
1740
|
+
model: options?.model,
|
|
1741
|
+
max_tokens: options?.maxTokens,
|
|
1742
|
+
temperature: options?.temperature
|
|
1743
|
+
},
|
|
1744
|
+
"ai"
|
|
1745
|
+
);
|
|
1746
|
+
} catch (err) {
|
|
1747
|
+
if (err instanceof BrokrError) err.component = "AIStream";
|
|
1748
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1749
|
+
capture("error", `AI stream failed: ${msg}`, "brokr.ai.stream", err instanceof Error ? err.stack : void 0);
|
|
1750
|
+
try {
|
|
1751
|
+
const fallback = await this.chat(input, options);
|
|
1752
|
+
yield fallback.content;
|
|
1753
|
+
} catch (fallbackErr) {
|
|
1754
|
+
throw err;
|
|
1755
|
+
}
|
|
1756
|
+
return;
|
|
1757
|
+
}
|
|
1758
|
+
const ct = res.headers.get("content-type") ?? "";
|
|
1759
|
+
if (!ct.includes("text/event-stream")) {
|
|
1760
|
+
try {
|
|
1761
|
+
const data = await res.json();
|
|
1762
|
+
const content = data.choices?.[0]?.message?.content ?? "";
|
|
1763
|
+
if (content) yield content;
|
|
1764
|
+
} catch {
|
|
1765
|
+
const fallback = await this.chat(input, options);
|
|
1766
|
+
yield fallback.content;
|
|
1767
|
+
}
|
|
1768
|
+
return;
|
|
1769
|
+
}
|
|
1770
|
+
const reader = res.body.getReader();
|
|
1771
|
+
const decoder = new TextDecoder();
|
|
1772
|
+
let buffer2 = "";
|
|
1773
|
+
while (true) {
|
|
1774
|
+
const { done, value } = await reader.read();
|
|
1775
|
+
if (done) break;
|
|
1776
|
+
buffer2 += decoder.decode(value, { stream: true });
|
|
1777
|
+
const lines = buffer2.split("\n");
|
|
1778
|
+
buffer2 = lines.pop() ?? "";
|
|
1779
|
+
for (const line of lines) {
|
|
1780
|
+
if (!line.startsWith("data: ")) continue;
|
|
1781
|
+
const payload = line.slice(6).trim();
|
|
1782
|
+
if (payload === "[DONE]") return;
|
|
1783
|
+
try {
|
|
1784
|
+
const parsed = JSON.parse(payload);
|
|
1785
|
+
const delta = parsed.choices?.[0]?.delta?.content ?? "";
|
|
1786
|
+
if (delta) yield delta;
|
|
1787
|
+
} catch {
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
// ---------------------------------------------------------------------------
|
|
1793
|
+
// Higher-level primitives
|
|
1794
|
+
// ---------------------------------------------------------------------------
|
|
1795
|
+
/**
|
|
1796
|
+
* Extract structured data from a prompt using JSON mode.
|
|
1797
|
+
* Returns a parsed object matching the provided schema shape.
|
|
1798
|
+
*/
|
|
1799
|
+
async structured(params) {
|
|
1800
|
+
requireToken(this._token, "ai");
|
|
1801
|
+
const schemaStr = JSON.stringify(params.schema, null, 2);
|
|
1802
|
+
const messages = [
|
|
1803
|
+
{
|
|
1804
|
+
role: "system",
|
|
1805
|
+
content: `You are a structured data extraction assistant. Return ONLY valid JSON matching this schema:
|
|
1806
|
+
${schemaStr}
|
|
1807
|
+
Do not include any other text, markdown, or explanation.`
|
|
1808
|
+
},
|
|
1809
|
+
{ role: "user", content: params.prompt }
|
|
1810
|
+
];
|
|
1811
|
+
try {
|
|
1812
|
+
const data = await gatewayFetch(this._gatewayUrl, this._token, "/v1/chat/completions", {
|
|
1813
|
+
messages,
|
|
1814
|
+
model: params.model,
|
|
1815
|
+
temperature: params.temperature ?? 0,
|
|
1816
|
+
response_format: { type: "json_object" }
|
|
1817
|
+
}, "ai");
|
|
1818
|
+
const raw = data.choices?.[0]?.message?.content ?? "{}";
|
|
1819
|
+
try {
|
|
1820
|
+
return JSON.parse(raw);
|
|
1821
|
+
} catch {
|
|
1822
|
+
throw new BrokrError(
|
|
1823
|
+
"[brokr] AI returned invalid JSON for structured extraction.",
|
|
1824
|
+
"AI_STRUCTURED_PARSE_ERROR",
|
|
1825
|
+
"ai"
|
|
1826
|
+
);
|
|
1827
|
+
}
|
|
1828
|
+
} catch (err) {
|
|
1829
|
+
if (err instanceof BrokrError) err.component = "AIStructured";
|
|
1830
|
+
throw err;
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
/**
|
|
1834
|
+
* Extract fields from unstructured text.
|
|
1835
|
+
* Semantic alias for structured() — same behavior, clearer intent.
|
|
1836
|
+
*/
|
|
1837
|
+
async extract(prompt, schema) {
|
|
1838
|
+
return this.structured({ prompt, schema });
|
|
1839
|
+
}
|
|
1840
|
+
/**
|
|
1841
|
+
* Summarize text using AI.
|
|
1842
|
+
*/
|
|
1843
|
+
async summarize(text, options) {
|
|
1844
|
+
try {
|
|
1845
|
+
const lengthHint = options?.maxLength ? ` Keep it under ${options.maxLength}.` : "";
|
|
1846
|
+
const response = await this.chat(
|
|
1847
|
+
[
|
|
1848
|
+
{
|
|
1849
|
+
role: "system",
|
|
1850
|
+
content: `You are a summarization assistant. Provide a concise, accurate summary of the following text.${lengthHint} Return only the summary, no preamble.`
|
|
1851
|
+
},
|
|
1852
|
+
{ role: "user", content: text }
|
|
1853
|
+
],
|
|
1854
|
+
{ model: options?.model }
|
|
1855
|
+
);
|
|
1856
|
+
return { summary: response.content };
|
|
1857
|
+
} catch (err) {
|
|
1858
|
+
if (err instanceof BrokrError) err.component = "AISummarize";
|
|
1859
|
+
throw err;
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
/**
|
|
1863
|
+
* Classify text into one of the provided labels.
|
|
1864
|
+
*/
|
|
1865
|
+
async classify(text, labels, options) {
|
|
1866
|
+
try {
|
|
1867
|
+
const labelsStr = labels.map((l) => `"${l}"`).join(", ");
|
|
1868
|
+
const result = await this.structured({
|
|
1869
|
+
prompt: `Classify the following text into exactly one of these labels: [${labelsStr}]
|
|
1870
|
+
|
|
1871
|
+
Text: ${text}`,
|
|
1872
|
+
schema: {
|
|
1873
|
+
type: "object",
|
|
1874
|
+
properties: {
|
|
1875
|
+
label: { type: "string", enum: labels },
|
|
1876
|
+
confidence: { type: "number", minimum: 0, maximum: 1 }
|
|
1877
|
+
},
|
|
1878
|
+
required: ["label", "confidence"]
|
|
1879
|
+
},
|
|
1880
|
+
model: options?.model,
|
|
1881
|
+
temperature: 0
|
|
1882
|
+
});
|
|
1883
|
+
return result;
|
|
1884
|
+
} catch (err) {
|
|
1885
|
+
if (err instanceof BrokrError) err.component = "AIClassify";
|
|
1886
|
+
throw err;
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
/**
|
|
1890
|
+
* Generate an embedding vector for the given text.
|
|
1891
|
+
*/
|
|
1892
|
+
async embed(text) {
|
|
1893
|
+
requireToken(this._token, "ai");
|
|
1894
|
+
try {
|
|
1895
|
+
const data = await gatewayFetch(this._gatewayUrl, this._token, "/v1/embeddings", {
|
|
1896
|
+
input: text,
|
|
1897
|
+
model: "text-embedding-3-small"
|
|
1898
|
+
}, "ai");
|
|
1899
|
+
return {
|
|
1900
|
+
vector: data.data?.[0]?.embedding ?? []
|
|
1901
|
+
};
|
|
1902
|
+
} catch (err) {
|
|
1903
|
+
if (err instanceof BrokrError) err.component = "AIEmbed";
|
|
1904
|
+
throw err;
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
/**
|
|
1908
|
+
* Generate an image from a text prompt.
|
|
1909
|
+
*/
|
|
1910
|
+
async image(prompt, options) {
|
|
1911
|
+
requireToken(this._token, "ai");
|
|
1912
|
+
try {
|
|
1913
|
+
const data = await gatewayFetch(this._gatewayUrl, this._token, "/v1/images/generate", {
|
|
1914
|
+
prompt,
|
|
1915
|
+
size: options?.size ?? "1024x1024",
|
|
1916
|
+
n: options?.n ?? 1,
|
|
1917
|
+
model: options?.model
|
|
1918
|
+
}, "ai");
|
|
1919
|
+
const url = data.data?.[0]?.url;
|
|
1920
|
+
if (!url) {
|
|
1921
|
+
throw new BrokrError("[brokr] Image generation returned no URL.", "AI_IMAGE_FAILED", "ai");
|
|
1922
|
+
}
|
|
1923
|
+
return { url };
|
|
1924
|
+
} catch (err) {
|
|
1925
|
+
if (err instanceof BrokrError) err.component = "AIImage";
|
|
1926
|
+
throw err;
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
// ---------------------------------------------------------------------------
|
|
1930
|
+
// OpenAI-SDK compatibility
|
|
1931
|
+
// ---------------------------------------------------------------------------
|
|
1932
|
+
/** OpenAI-SDK compatible base URL. */
|
|
1933
|
+
get baseURL() {
|
|
1934
|
+
return `${this._gatewayUrl}/v1`;
|
|
1935
|
+
}
|
|
1936
|
+
/** Use as `apiKey` with the official OpenAI SDK to route through Brokr's gateway. */
|
|
1937
|
+
get apiKey() {
|
|
1938
|
+
requireToken(this._token, "ai");
|
|
1939
|
+
return this._token;
|
|
1940
|
+
}
|
|
1941
|
+
};
|
|
1942
|
+
|
|
1943
|
+
// src/notifications/client.ts
|
|
1944
|
+
var BrokrNotificationsClient = class {
|
|
1945
|
+
constructor(token, gatewayUrl) {
|
|
1946
|
+
this.token = token;
|
|
1947
|
+
this.gatewayUrl = gatewayUrl;
|
|
1948
|
+
}
|
|
1949
|
+
/**
|
|
1950
|
+
* Send a notification to a user. Delivered via WebSocket in real-time
|
|
1951
|
+
* and persisted in the notification history.
|
|
1952
|
+
*/
|
|
1953
|
+
async send(params) {
|
|
1954
|
+
requireToken(this.token, "notifications");
|
|
1955
|
+
try {
|
|
1956
|
+
return await gatewayFetch(
|
|
1957
|
+
this.gatewayUrl,
|
|
1958
|
+
this.token,
|
|
1959
|
+
"/v1/notifications/push",
|
|
1960
|
+
params,
|
|
1961
|
+
"notifications"
|
|
1962
|
+
);
|
|
1963
|
+
} catch (err) {
|
|
1964
|
+
if (err instanceof BrokrError) err.component = "Notifications";
|
|
1965
|
+
throw err;
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
/**
|
|
1969
|
+
* Fetch notification history for a user. Reads from the DO's SQLite
|
|
1970
|
+
* storage via a gateway REST endpoint.
|
|
1971
|
+
*/
|
|
1972
|
+
async list(params) {
|
|
1973
|
+
requireToken(this.token, "notifications");
|
|
1974
|
+
try {
|
|
1975
|
+
return await gatewayFetch(
|
|
1976
|
+
this.gatewayUrl,
|
|
1977
|
+
this.token,
|
|
1978
|
+
"/v1/notifications/list",
|
|
1979
|
+
params,
|
|
1980
|
+
"notifications"
|
|
1981
|
+
);
|
|
1982
|
+
} catch (err) {
|
|
1983
|
+
if (err instanceof BrokrError) err.component = "Notifications";
|
|
1984
|
+
throw err;
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
};
|
|
1988
|
+
|
|
1989
|
+
// src/models.ts
|
|
1990
|
+
var providers = [
|
|
1991
|
+
{ id: "deepseek", label: "Deepseek", model: "deepseek-chat", color: "#0EA5E9", free: true, logo: "https://assets.brokr.sh/sdk/deepseek_logo.png" },
|
|
1992
|
+
{ id: "openai", label: "ChatGPT", model: "gpt-5.4-mini", color: "#10B981", free: false, logo: "https://assets.brokr.sh/sdk/gpt_logo.png" },
|
|
1993
|
+
{ id: "anthropic", label: "Claude", model: "claude-sonnet-4-6", color: "#F59E0B", free: false, logo: "https://assets.brokr.sh/sdk/claude_logo.png" },
|
|
1994
|
+
{ id: "kimi", label: "Kimi", model: "fireworks/kimi-k2p5", color: "#8B5CF6", free: false, logo: "https://assets.brokr.sh/sdk/kimi_logo.png" },
|
|
1995
|
+
{ id: "minimax", label: "Minimax", model: "MiniMaxAI/MiniMax-M2.5", color: "#EC4899", free: false, logo: "https://assets.brokr.sh/sdk/minimax_logo.png" }
|
|
1996
|
+
];
|
|
1997
|
+
function resolveProviderByModel(model) {
|
|
1998
|
+
return providers.find((p) => p.model === model) ?? providers.find((p) => model.toLowerCase().includes(p.id));
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
// src/next/notifications.ts
|
|
2002
|
+
var DEFAULT_TABLE = "brokr_notifications";
|
|
2003
|
+
async function getNeonSql() {
|
|
2004
|
+
const url = process.env.DATABASE_URL;
|
|
2005
|
+
if (!url) {
|
|
2006
|
+
throw new Error(
|
|
2007
|
+
"[brokr] DATABASE_URL is not set. Notification persistence requires a Postgres database.\nRun: brokr add database"
|
|
2008
|
+
);
|
|
2009
|
+
}
|
|
2010
|
+
try {
|
|
2011
|
+
const { neon } = await import("@neondatabase/serverless");
|
|
2012
|
+
return neon(url);
|
|
2013
|
+
} catch {
|
|
2014
|
+
throw new Error(
|
|
2015
|
+
"[brokr] @neondatabase/serverless is not installed.\nRun: npm install @neondatabase/serverless"
|
|
2016
|
+
);
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
var _sql = null;
|
|
2020
|
+
var _ensuredTables = /* @__PURE__ */ new Set();
|
|
2021
|
+
async function getDb() {
|
|
2022
|
+
if (!_sql) _sql = await getNeonSql();
|
|
2023
|
+
return _sql;
|
|
2024
|
+
}
|
|
2025
|
+
function validateTableName(name) {
|
|
2026
|
+
if (!/^[a-z_][a-z0-9_]{0,62}$/.test(name)) {
|
|
2027
|
+
throw new Error(`[brokr] Invalid table name: "${name}". Use lowercase letters, digits, and underscores only.`);
|
|
2028
|
+
}
|
|
2029
|
+
return name;
|
|
2030
|
+
}
|
|
2031
|
+
async function ensureNotificationTable(tableName = DEFAULT_TABLE) {
|
|
2032
|
+
const table = validateTableName(tableName);
|
|
2033
|
+
if (_ensuredTables.has(table)) return;
|
|
2034
|
+
const sql = await getDb();
|
|
2035
|
+
await sql`
|
|
2036
|
+
CREATE TABLE IF NOT EXISTS brokr_notifications (
|
|
2037
|
+
id TEXT PRIMARY KEY,
|
|
2038
|
+
user_id TEXT NOT NULL,
|
|
2039
|
+
type TEXT NOT NULL DEFAULT 'default',
|
|
2040
|
+
title TEXT NOT NULL,
|
|
2041
|
+
message TEXT NOT NULL,
|
|
2042
|
+
variant TEXT NOT NULL DEFAULT 'default',
|
|
2043
|
+
href TEXT,
|
|
2044
|
+
image_url TEXT,
|
|
2045
|
+
image_alt TEXT,
|
|
2046
|
+
read BOOLEAN NOT NULL DEFAULT false,
|
|
2047
|
+
read_at TIMESTAMPTZ,
|
|
2048
|
+
data JSONB,
|
|
2049
|
+
expires_at TIMESTAMPTZ,
|
|
2050
|
+
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
2051
|
+
)
|
|
2052
|
+
`;
|
|
2053
|
+
await sql`ALTER TABLE brokr_notifications ADD COLUMN IF NOT EXISTS type TEXT NOT NULL DEFAULT 'default'`.catch(() => {
|
|
2054
|
+
});
|
|
2055
|
+
await sql`ALTER TABLE brokr_notifications ADD COLUMN IF NOT EXISTS href TEXT`.catch(() => {
|
|
2056
|
+
});
|
|
2057
|
+
await sql`ALTER TABLE brokr_notifications ADD COLUMN IF NOT EXISTS image_url TEXT`.catch(() => {
|
|
2058
|
+
});
|
|
2059
|
+
await sql`ALTER TABLE brokr_notifications ADD COLUMN IF NOT EXISTS image_alt TEXT`.catch(() => {
|
|
2060
|
+
});
|
|
2061
|
+
await sql`ALTER TABLE brokr_notifications ADD COLUMN IF NOT EXISTS read_at TIMESTAMPTZ`.catch(() => {
|
|
2062
|
+
});
|
|
2063
|
+
await sql`ALTER TABLE brokr_notifications ADD COLUMN IF NOT EXISTS expires_at TIMESTAMPTZ`.catch(() => {
|
|
2064
|
+
});
|
|
2065
|
+
await sql`
|
|
2066
|
+
CREATE INDEX IF NOT EXISTS idx_brokr_notifications_user
|
|
2067
|
+
ON brokr_notifications(user_id, created_at DESC)
|
|
2068
|
+
`;
|
|
2069
|
+
await sql`
|
|
2070
|
+
CREATE INDEX IF NOT EXISTS idx_brokr_notifications_type
|
|
2071
|
+
ON brokr_notifications(user_id, type, created_at DESC)
|
|
2072
|
+
`;
|
|
2073
|
+
if (table !== DEFAULT_TABLE) {
|
|
2074
|
+
const exists = await sql`
|
|
2075
|
+
SELECT 1 FROM information_schema.tables
|
|
2076
|
+
WHERE table_schema = 'public' AND table_name = ${table}
|
|
2077
|
+
`;
|
|
2078
|
+
if (exists.length === 0) {
|
|
2079
|
+
throw new Error(
|
|
2080
|
+
`[brokr] Custom notification table "${table}" does not exist.
|
|
2081
|
+
Create it with the same schema as brokr_notifications, or remove the tableName config.`
|
|
2082
|
+
);
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
_ensuredTables.add(table);
|
|
2086
|
+
}
|
|
2087
|
+
async function persistNotification(params, opts) {
|
|
2088
|
+
await ensureNotificationTable(opts?.tableName);
|
|
2089
|
+
const sql = await getDb();
|
|
2090
|
+
await sql`
|
|
2091
|
+
INSERT INTO brokr_notifications (id, user_id, type, title, message, variant, href, image_url, image_alt, data, expires_at, created_at)
|
|
2092
|
+
VALUES (
|
|
2093
|
+
${params.id},
|
|
2094
|
+
${params.userId},
|
|
2095
|
+
${params.type ?? "default"},
|
|
2096
|
+
${params.title},
|
|
2097
|
+
${params.message},
|
|
2098
|
+
${params.variant},
|
|
2099
|
+
${params.href ?? null},
|
|
2100
|
+
${params.imageUrl ?? null},
|
|
2101
|
+
${params.imageAlt ?? null},
|
|
2102
|
+
${params.data ? JSON.stringify(params.data) : null}::jsonb,
|
|
2103
|
+
${params.expiresAt?.toISOString() ?? null},
|
|
2104
|
+
NOW()
|
|
2105
|
+
)
|
|
2106
|
+
ON CONFLICT (id) DO NOTHING
|
|
2107
|
+
`;
|
|
2108
|
+
}
|
|
2109
|
+
async function listPersistedNotifications(userId, params, opts) {
|
|
2110
|
+
await ensureNotificationTable(opts?.tableName);
|
|
2111
|
+
const sql = await getDb();
|
|
2112
|
+
const limit = Math.min(params?.limit ?? 50, 100);
|
|
2113
|
+
const typeFilter = params?.type ?? null;
|
|
2114
|
+
const skipUnreadFilter = !(params?.unreadOnly ?? false);
|
|
2115
|
+
const rows = await sql`
|
|
2116
|
+
SELECT id, user_id, type, title, message, variant, href, image_url, image_alt, read, read_at, data, expires_at, created_at
|
|
2117
|
+
FROM brokr_notifications
|
|
2118
|
+
WHERE user_id = ${userId}
|
|
2119
|
+
AND (expires_at IS NULL OR expires_at > NOW())
|
|
2120
|
+
AND (${typeFilter}::text IS NULL OR type = ${typeFilter})
|
|
2121
|
+
AND (${skipUnreadFilter}::boolean OR read = false)
|
|
2122
|
+
ORDER BY created_at DESC
|
|
2123
|
+
LIMIT ${limit}
|
|
2124
|
+
`;
|
|
2125
|
+
return rows.map((r) => ({
|
|
2126
|
+
id: r.id,
|
|
2127
|
+
userId: r.user_id,
|
|
2128
|
+
type: r.type ?? "default",
|
|
2129
|
+
title: r.title,
|
|
2130
|
+
message: r.message,
|
|
2131
|
+
variant: r.variant,
|
|
2132
|
+
href: r.href ?? null,
|
|
2133
|
+
imageUrl: r.image_url ?? null,
|
|
2134
|
+
imageAlt: r.image_alt ?? null,
|
|
2135
|
+
read: r.read,
|
|
2136
|
+
readAt: r.read_at ? r.read_at.toISOString() : null,
|
|
2137
|
+
data: r.data,
|
|
2138
|
+
expiresAt: r.expires_at ? r.expires_at.toISOString() : null,
|
|
2139
|
+
createdAt: r.created_at.toISOString()
|
|
2140
|
+
}));
|
|
2141
|
+
}
|
|
2142
|
+
async function markNotificationRead(notificationId, userId, opts) {
|
|
2143
|
+
await ensureNotificationTable(opts?.tableName);
|
|
2144
|
+
const sql = await getDb();
|
|
2145
|
+
await sql`
|
|
2146
|
+
UPDATE brokr_notifications SET read = true, read_at = NOW()
|
|
2147
|
+
WHERE id = ${notificationId} AND user_id = ${userId}
|
|
2148
|
+
`;
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
// src/ai/types.ts
|
|
2152
|
+
function contentToText(content) {
|
|
2153
|
+
if (typeof content === "string") return content;
|
|
2154
|
+
return content.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
// src/next/chat.ts
|
|
2158
|
+
initCapture();
|
|
2159
|
+
var CHARS_PER_TOKEN = 4;
|
|
2160
|
+
var DEFAULT_TOKEN_BUDGET = 2e4;
|
|
2161
|
+
function estimateTokens(text) {
|
|
2162
|
+
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
2163
|
+
}
|
|
2164
|
+
function trimToTokenBudget(msgs, budget = DEFAULT_TOKEN_BUDGET) {
|
|
2165
|
+
if (msgs.length === 0) return msgs;
|
|
2166
|
+
const system = msgs.filter((m) => m.role === "system");
|
|
2167
|
+
const rest = msgs.filter((m) => m.role !== "system");
|
|
2168
|
+
if (rest.length === 0) return system;
|
|
2169
|
+
const systemCost = system.reduce((s, m) => s + estimateTokens(contentToText(m.content)), 0);
|
|
2170
|
+
const latest = rest[rest.length - 1];
|
|
2171
|
+
const latestCost = estimateTokens(contentToText(latest.content));
|
|
2172
|
+
const remaining = budget - systemCost - latestCost;
|
|
2173
|
+
if (remaining <= 0) return [...system, latest];
|
|
2174
|
+
const kept = [];
|
|
2175
|
+
let used = 0;
|
|
2176
|
+
for (let i = rest.length - 2; i >= 0; i--) {
|
|
2177
|
+
const cost = estimateTokens(contentToText(rest[i].content));
|
|
2178
|
+
if (used + cost > remaining) break;
|
|
2179
|
+
used += cost;
|
|
2180
|
+
kept.unshift(rest[i]);
|
|
2181
|
+
}
|
|
2182
|
+
return [...system, ...kept, latest];
|
|
2183
|
+
}
|
|
2184
|
+
async function getNeonSql2() {
|
|
2185
|
+
const url = process.env.DATABASE_URL;
|
|
2186
|
+
if (!url) {
|
|
2187
|
+
throw new Error(
|
|
2188
|
+
"[brokr] DATABASE_URL is not set. Thread persistence requires a Postgres database.\nSet DATABASE_URL in your environment variables."
|
|
2189
|
+
);
|
|
2190
|
+
}
|
|
2191
|
+
try {
|
|
2192
|
+
const { neon } = await import("@neondatabase/serverless");
|
|
2193
|
+
return neon(url);
|
|
2194
|
+
} catch {
|
|
2195
|
+
throw new Error(
|
|
2196
|
+
"[brokr] @neondatabase/serverless is not installed.\nRun: npm install @neondatabase/serverless"
|
|
2197
|
+
);
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
var _sql2 = null;
|
|
2201
|
+
var _tablesEnsured = false;
|
|
2202
|
+
async function getDb2() {
|
|
2203
|
+
if (!_sql2) _sql2 = await getNeonSql2();
|
|
2204
|
+
return _sql2;
|
|
2205
|
+
}
|
|
2206
|
+
async function ensureTables(sql) {
|
|
2207
|
+
if (_tablesEnsured) return;
|
|
2208
|
+
await sql`CREATE SCHEMA IF NOT EXISTS brokr_ai`;
|
|
2209
|
+
const oldTablesExist = await sql`
|
|
2210
|
+
SELECT EXISTS (
|
|
2211
|
+
SELECT 1 FROM information_schema.tables
|
|
2212
|
+
WHERE table_schema = 'public' AND table_name = 'brokr_ai_threads'
|
|
2213
|
+
) AS exists
|
|
2214
|
+
`;
|
|
2215
|
+
if (oldTablesExist[0]?.exists) {
|
|
2216
|
+
await sql`ALTER TABLE public.brokr_ai_threads SET SCHEMA brokr_ai`.catch(() => {
|
|
2217
|
+
});
|
|
2218
|
+
await sql`ALTER TABLE public.brokr_ai_messages SET SCHEMA brokr_ai`.catch(() => {
|
|
2219
|
+
});
|
|
2220
|
+
await sql`ALTER TABLE brokr_ai.brokr_ai_threads RENAME TO threads`.catch(() => {
|
|
2221
|
+
});
|
|
2222
|
+
await sql`ALTER TABLE brokr_ai.brokr_ai_messages RENAME TO messages`.catch(() => {
|
|
2223
|
+
});
|
|
2224
|
+
}
|
|
2225
|
+
await sql`
|
|
2226
|
+
CREATE TABLE IF NOT EXISTS brokr_ai.threads (
|
|
2227
|
+
id TEXT PRIMARY KEY,
|
|
2228
|
+
surface TEXT NOT NULL DEFAULT 'default',
|
|
2229
|
+
subject TEXT,
|
|
2230
|
+
title TEXT NOT NULL DEFAULT 'New chat',
|
|
2231
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
2232
|
+
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
2233
|
+
)
|
|
2234
|
+
`;
|
|
2235
|
+
await sql`
|
|
2236
|
+
CREATE TABLE IF NOT EXISTS brokr_ai.messages (
|
|
2237
|
+
id TEXT PRIMARY KEY,
|
|
2238
|
+
thread_id TEXT NOT NULL REFERENCES brokr_ai.threads(id) ON DELETE CASCADE,
|
|
2239
|
+
role TEXT NOT NULL CHECK (role IN ('user', 'assistant')),
|
|
2240
|
+
content TEXT NOT NULL,
|
|
2241
|
+
model TEXT,
|
|
2242
|
+
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
2243
|
+
)
|
|
2244
|
+
`;
|
|
2245
|
+
await sql`
|
|
2246
|
+
ALTER TABLE brokr_ai.messages ADD COLUMN IF NOT EXISTS model TEXT
|
|
2247
|
+
`.catch(() => {
|
|
2248
|
+
});
|
|
2249
|
+
await sql`
|
|
2250
|
+
ALTER TABLE brokr_ai.messages ADD COLUMN IF NOT EXISTS status TEXT NOT NULL DEFAULT 'complete'
|
|
2251
|
+
`.catch(() => {
|
|
2252
|
+
});
|
|
2253
|
+
await sql`
|
|
2254
|
+
ALTER TABLE brokr_ai.messages ADD COLUMN IF NOT EXISTS provider TEXT
|
|
2255
|
+
`.catch(() => {
|
|
2256
|
+
});
|
|
2257
|
+
await sql`
|
|
2258
|
+
CREATE INDEX IF NOT EXISTS brokr_ai_threads_surface_subject
|
|
2259
|
+
ON brokr_ai.threads(surface, subject, updated_at DESC)
|
|
2260
|
+
`;
|
|
2261
|
+
await sql`
|
|
2262
|
+
CREATE INDEX IF NOT EXISTS brokr_ai_messages_thread_id
|
|
2263
|
+
ON brokr_ai.messages(thread_id, created_at ASC)
|
|
2264
|
+
`;
|
|
2265
|
+
_tablesEnsured = true;
|
|
2266
|
+
}
|
|
2267
|
+
function parsePath(url) {
|
|
2268
|
+
const segments = new URL(url).pathname.split("/").filter(Boolean);
|
|
2269
|
+
const last = segments[segments.length - 1];
|
|
2270
|
+
const secondLast = segments[segments.length - 2];
|
|
2271
|
+
if (last === "chat") return { action: "chat" };
|
|
2272
|
+
if (last === "threads") return { action: "threads" };
|
|
2273
|
+
if (last === "ws-token") return { action: "ws-token" };
|
|
2274
|
+
if (last === "notifications") return { action: "notifications" };
|
|
2275
|
+
if (last === "read" && secondLast === "notifications") return { action: "notifications-read" };
|
|
2276
|
+
if (last === "messages" && secondLast) return { action: "messages", id: secondLast };
|
|
2277
|
+
if (last === "rename" && secondLast) return { action: "rename", id: secondLast };
|
|
2278
|
+
if (secondLast === "threads" && last) return { action: "thread", id: last };
|
|
2279
|
+
return { action: "unknown" };
|
|
2280
|
+
}
|
|
2281
|
+
function makeThreadTitle(content) {
|
|
2282
|
+
const cleaned = content.trim().replace(/\s+/g, " ");
|
|
2283
|
+
return cleaned.length > 48 ? `${cleaned.slice(0, 48).trimEnd()}\u2026` : cleaned || "New chat";
|
|
2284
|
+
}
|
|
2285
|
+
async function handleListThreads(request, opts) {
|
|
2286
|
+
if (!opts.persist) {
|
|
2287
|
+
return Response.json({ threads: [] });
|
|
2288
|
+
}
|
|
2289
|
+
const url = new URL(request.url);
|
|
2290
|
+
const surface = url.searchParams.get("surface") ?? "default";
|
|
2291
|
+
const subject = url.searchParams.get("subject");
|
|
2292
|
+
const sql = await getDb2();
|
|
2293
|
+
await ensureTables(sql);
|
|
2294
|
+
const rows = subject ? await sql`
|
|
2295
|
+
SELECT t.id, t.surface, t.subject, t.title, t.created_at, t.updated_at,
|
|
2296
|
+
(SELECT COUNT(*) FROM brokr_ai.messages WHERE thread_id = t.id) AS message_count
|
|
2297
|
+
FROM brokr_ai.threads t
|
|
2298
|
+
WHERE t.surface = ${surface} AND t.subject = ${subject}
|
|
2299
|
+
ORDER BY t.updated_at DESC
|
|
2300
|
+
LIMIT 50
|
|
2301
|
+
` : await sql`
|
|
2302
|
+
SELECT t.id, t.surface, t.subject, t.title, t.created_at, t.updated_at,
|
|
2303
|
+
(SELECT COUNT(*) FROM brokr_ai.messages WHERE thread_id = t.id) AS message_count
|
|
2304
|
+
FROM brokr_ai.threads t
|
|
2305
|
+
WHERE t.surface = ${surface}
|
|
2306
|
+
ORDER BY t.updated_at DESC
|
|
2307
|
+
LIMIT 50
|
|
2308
|
+
`;
|
|
2309
|
+
return Response.json({
|
|
2310
|
+
threads: rows.map((r) => ({
|
|
2311
|
+
id: r.id,
|
|
2312
|
+
surface: r.surface,
|
|
2313
|
+
subject: r.subject,
|
|
2314
|
+
title: r.title,
|
|
2315
|
+
createdAt: r.created_at,
|
|
2316
|
+
updatedAt: r.updated_at,
|
|
2317
|
+
messageCount: Number(r.message_count)
|
|
2318
|
+
}))
|
|
2319
|
+
});
|
|
2320
|
+
}
|
|
2321
|
+
async function handleGetMessages(request, threadId, opts) {
|
|
2322
|
+
if (!opts.persist) {
|
|
2323
|
+
return Response.json({ messages: [], hasMore: false });
|
|
2324
|
+
}
|
|
2325
|
+
const url = new URL(request.url);
|
|
2326
|
+
const limit = Math.min(Number(url.searchParams.get("limit")) || 30, 100);
|
|
2327
|
+
const offset = Math.max(Number(url.searchParams.get("offset")) || 0, 0);
|
|
2328
|
+
const sql = await getDb2();
|
|
2329
|
+
const countResult = await sql`
|
|
2330
|
+
SELECT COUNT(*) AS total FROM brokr_ai.messages WHERE thread_id = ${threadId}
|
|
2331
|
+
`;
|
|
2332
|
+
const total = Number(countResult[0]?.total ?? 0);
|
|
2333
|
+
const rows = await sql`
|
|
2334
|
+
SELECT id, role, content, model, status, provider FROM (
|
|
2335
|
+
SELECT id, role, content, model, status, provider, created_at
|
|
2336
|
+
FROM brokr_ai.messages
|
|
2337
|
+
WHERE thread_id = ${threadId}
|
|
2338
|
+
ORDER BY created_at DESC, id DESC
|
|
2339
|
+
LIMIT ${limit} OFFSET ${offset}
|
|
2340
|
+
) sub ORDER BY created_at ASC, id ASC
|
|
2341
|
+
`;
|
|
2342
|
+
return Response.json({
|
|
2343
|
+
messages: rows.map((r) => ({
|
|
2344
|
+
id: r.id,
|
|
2345
|
+
role: r.role,
|
|
2346
|
+
content: r.content,
|
|
2347
|
+
model: r.model ?? void 0,
|
|
2348
|
+
status: r.status ?? "complete",
|
|
2349
|
+
provider: r.provider ?? void 0
|
|
2350
|
+
})),
|
|
2351
|
+
hasMore: offset + limit < total
|
|
2352
|
+
});
|
|
2353
|
+
}
|
|
2354
|
+
async function handleRenameThread(request, threadId, opts) {
|
|
2355
|
+
if (!opts.persist) {
|
|
2356
|
+
return Response.json({ success: false }, { status: 400 });
|
|
2357
|
+
}
|
|
2358
|
+
const body = await request.json();
|
|
2359
|
+
const title = (body.title ?? "").trim();
|
|
2360
|
+
if (!title) {
|
|
2361
|
+
return Response.json({ error: "Title is required" }, { status: 400 });
|
|
2362
|
+
}
|
|
2363
|
+
const safeTitle = title.length > 100 ? `${title.slice(0, 100).trimEnd()}\u2026` : title;
|
|
2364
|
+
const sql = await getDb2();
|
|
2365
|
+
await ensureTables(sql);
|
|
2366
|
+
await sql`UPDATE brokr_ai.threads SET title = ${safeTitle}, updated_at = NOW() WHERE id = ${threadId}`;
|
|
2367
|
+
return Response.json({ success: true, title: safeTitle });
|
|
2368
|
+
}
|
|
2369
|
+
async function handleDeleteThread(_request, threadId, opts) {
|
|
2370
|
+
if (!opts.persist) {
|
|
2371
|
+
return Response.json({ success: true });
|
|
2372
|
+
}
|
|
2373
|
+
const sql = await getDb2();
|
|
2374
|
+
await sql`DELETE FROM brokr_ai.threads WHERE id = ${threadId}`;
|
|
2375
|
+
return Response.json({ success: true });
|
|
2376
|
+
}
|
|
2377
|
+
async function backgroundTask(promise) {
|
|
2378
|
+
try {
|
|
2379
|
+
const mod = await Promise.resolve().then(() => __toESM(require_functions()));
|
|
2380
|
+
if (typeof mod.waitUntil === "function") {
|
|
2381
|
+
mod.waitUntil(promise);
|
|
2382
|
+
return;
|
|
2383
|
+
}
|
|
2384
|
+
} catch {
|
|
2385
|
+
}
|
|
2386
|
+
promise.catch((err) => console.error("[brokr] Background task failed:", err));
|
|
2387
|
+
}
|
|
2388
|
+
async function handleChat(request, opts) {
|
|
2389
|
+
const body = await request.json();
|
|
2390
|
+
const gatewayUrl = process.env.BROKR_GATEWAY_URL ?? GATEWAY_URL;
|
|
2391
|
+
const token = process.env.BROKR_TOKEN;
|
|
2392
|
+
if (!token) {
|
|
2393
|
+
return Response.json(
|
|
2394
|
+
{ error: "BROKR_TOKEN is not configured. Run: brokr env pull --stack <name>" },
|
|
2395
|
+
{ status: 500 }
|
|
2396
|
+
);
|
|
2397
|
+
}
|
|
2398
|
+
const ai = new BrokrAIClient(token, gatewayUrl);
|
|
2399
|
+
const model = body.model;
|
|
2400
|
+
const systemPrompt = opts.systemPrompt ?? body.systemPrompt;
|
|
2401
|
+
const resolvedProvider = model ? resolveProviderByModel(model) : void 0;
|
|
2402
|
+
let messages;
|
|
2403
|
+
let threadId;
|
|
2404
|
+
let isNewThread = false;
|
|
2405
|
+
let userMsgId;
|
|
2406
|
+
let assistantMsgId;
|
|
2407
|
+
let threadTitle;
|
|
2408
|
+
if (opts.persist && body.content !== void 0) {
|
|
2409
|
+
threadId = body.thread_id ?? void 0;
|
|
2410
|
+
const sql = await getDb2();
|
|
2411
|
+
await ensureTables(sql);
|
|
2412
|
+
let history = [];
|
|
2413
|
+
if (threadId) {
|
|
2414
|
+
const [historyRows, titleRows] = await Promise.all([
|
|
2415
|
+
sql`
|
|
2416
|
+
SELECT role, content FROM brokr_ai.messages
|
|
2417
|
+
WHERE thread_id = ${threadId} AND status = 'complete'
|
|
2418
|
+
ORDER BY created_at ASC
|
|
2419
|
+
`,
|
|
2420
|
+
sql`SELECT title FROM brokr_ai.threads WHERE id = ${threadId} LIMIT 1`
|
|
2421
|
+
]);
|
|
2422
|
+
history = historyRows;
|
|
2423
|
+
threadTitle = titleRows[0]?.title || void 0;
|
|
2424
|
+
} else {
|
|
2425
|
+
threadId = crypto.randomUUID();
|
|
2426
|
+
isNewThread = true;
|
|
2427
|
+
}
|
|
2428
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2429
|
+
const assistantNow = new Date(Date.parse(now) + 1).toISOString();
|
|
2430
|
+
if (!threadTitle) {
|
|
2431
|
+
threadTitle = makeThreadTitle(body.content ?? "");
|
|
2432
|
+
}
|
|
2433
|
+
userMsgId = crypto.randomUUID();
|
|
2434
|
+
assistantMsgId = crypto.randomUUID();
|
|
2435
|
+
if (isNewThread) {
|
|
2436
|
+
await sql`
|
|
2437
|
+
INSERT INTO brokr_ai.threads (id, surface, subject, title, created_at, updated_at)
|
|
2438
|
+
VALUES (
|
|
2439
|
+
${threadId},
|
|
2440
|
+
${body.surface ?? "default"},
|
|
2441
|
+
${body.subject ?? null},
|
|
2442
|
+
${threadTitle},
|
|
2443
|
+
${now},
|
|
2444
|
+
${now}
|
|
2445
|
+
)
|
|
2446
|
+
`;
|
|
2447
|
+
} else {
|
|
2448
|
+
await sql`
|
|
2449
|
+
UPDATE brokr_ai.threads SET updated_at = ${now} WHERE id = ${threadId}
|
|
2450
|
+
`;
|
|
2451
|
+
}
|
|
2452
|
+
await sql`
|
|
2453
|
+
INSERT INTO brokr_ai.messages (id, thread_id, role, content, status, created_at)
|
|
2454
|
+
VALUES (${userMsgId}, ${threadId}, 'user', ${body.content ?? ""}, 'complete', ${now})
|
|
2455
|
+
`;
|
|
2456
|
+
await sql`
|
|
2457
|
+
INSERT INTO brokr_ai.messages (id, thread_id, role, content, model, provider, status, created_at)
|
|
2458
|
+
VALUES (${assistantMsgId}, ${threadId}, 'assistant', '', ${model ?? null}, ${resolvedProvider?.id ?? null}, 'pending', ${assistantNow})
|
|
2459
|
+
`;
|
|
2460
|
+
messages = trimToTokenBudget([
|
|
2461
|
+
...systemPrompt ? [{ role: "system", content: systemPrompt }] : [],
|
|
2462
|
+
...history.map((h) => ({ role: h.role, content: h.content })),
|
|
2463
|
+
{ role: "user", content: body.content }
|
|
2464
|
+
]);
|
|
2465
|
+
} else {
|
|
2466
|
+
const clientMessages = body.messages ?? [];
|
|
2467
|
+
messages = systemPrompt ? [{ role: "system", content: systemPrompt }, ...clientMessages] : clientMessages;
|
|
2468
|
+
}
|
|
2469
|
+
const chunks = [];
|
|
2470
|
+
let chunkIndex = 0;
|
|
2471
|
+
let generationDone = false;
|
|
2472
|
+
let generationError = null;
|
|
2473
|
+
let fullResponse = "";
|
|
2474
|
+
let metadataSent = false;
|
|
2475
|
+
let resolveChunk = null;
|
|
2476
|
+
function notifyChunk() {
|
|
2477
|
+
if (resolveChunk) {
|
|
2478
|
+
const fn = resolveChunk;
|
|
2479
|
+
resolveChunk = null;
|
|
2480
|
+
fn();
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
function waitForChunk() {
|
|
2484
|
+
if (generationDone || generationError || chunkIndex < chunks.length) {
|
|
2485
|
+
return Promise.resolve();
|
|
2486
|
+
}
|
|
2487
|
+
return new Promise((resolve) => {
|
|
2488
|
+
resolveChunk = resolve;
|
|
2489
|
+
});
|
|
2490
|
+
}
|
|
2491
|
+
const generationTask = (async () => {
|
|
2492
|
+
try {
|
|
2493
|
+
const aiStream = ai.stream(messages, { model });
|
|
2494
|
+
for await (const delta of aiStream) {
|
|
2495
|
+
fullResponse += delta;
|
|
2496
|
+
chunks.push(delta);
|
|
2497
|
+
notifyChunk();
|
|
2498
|
+
}
|
|
2499
|
+
if (opts.persist && threadId && assistantMsgId) {
|
|
2500
|
+
const sql = await getDb2();
|
|
2501
|
+
await sql`
|
|
2502
|
+
UPDATE brokr_ai.messages
|
|
2503
|
+
SET content = ${fullResponse}, status = 'complete'
|
|
2504
|
+
WHERE id = ${assistantMsgId}
|
|
2505
|
+
`;
|
|
2506
|
+
}
|
|
2507
|
+
if (opts.persist && body.userId && threadId && assistantMsgId) {
|
|
2508
|
+
const notifId = crypto.randomUUID();
|
|
2509
|
+
const preview = fullResponse.length > 120 ? `${fullResponse.slice(0, 120)}...` : fullResponse;
|
|
2510
|
+
const notifData = {
|
|
2511
|
+
type: "ai_response",
|
|
2512
|
+
threadId,
|
|
2513
|
+
assistantMessageId: assistantMsgId,
|
|
2514
|
+
model: model ?? null,
|
|
2515
|
+
provider: resolvedProvider?.id ?? null,
|
|
2516
|
+
providerLogo: resolvedProvider?.logo ?? null,
|
|
2517
|
+
surface: body.surface ?? "default",
|
|
2518
|
+
href: `/chat?thread=${threadId}`
|
|
2519
|
+
};
|
|
2520
|
+
await Promise.allSettled([
|
|
2521
|
+
persistNotification({
|
|
2522
|
+
id: notifId,
|
|
2523
|
+
userId: body.userId,
|
|
2524
|
+
type: "ai_response",
|
|
2525
|
+
title: threadTitle ?? "AI Response",
|
|
2526
|
+
message: preview,
|
|
2527
|
+
variant: "default",
|
|
2528
|
+
href: `/chat?thread=${threadId}`,
|
|
2529
|
+
imageUrl: resolvedProvider?.logo ?? void 0,
|
|
2530
|
+
imageAlt: resolvedProvider?.label ?? void 0,
|
|
2531
|
+
data: notifData
|
|
2532
|
+
}, { tableName: opts.notifications?.tableName }).catch((err) => {
|
|
2533
|
+
console.error("[brokr] Notification DB persist failed:", err);
|
|
2534
|
+
}),
|
|
2535
|
+
new BrokrNotificationsClient(token, gatewayUrl).send({
|
|
2536
|
+
userId: body.userId,
|
|
2537
|
+
title: threadTitle ?? "AI Response",
|
|
2538
|
+
message: preview,
|
|
2539
|
+
variant: "default",
|
|
2540
|
+
data: notifData,
|
|
2541
|
+
dedup: `ai_response:${assistantMsgId}`
|
|
2542
|
+
}).catch((err) => {
|
|
2543
|
+
console.error("[brokr] Notification push failed:", err);
|
|
2544
|
+
})
|
|
2545
|
+
]);
|
|
2546
|
+
}
|
|
2547
|
+
} catch (err) {
|
|
2548
|
+
console.error("[brokr] AI generation error:", err);
|
|
2549
|
+
generationError = "Something went wrong generating a reply.";
|
|
2550
|
+
if (opts.persist && threadId && assistantMsgId) {
|
|
2551
|
+
try {
|
|
2552
|
+
const sql = await getDb2();
|
|
2553
|
+
await sql`
|
|
2554
|
+
UPDATE brokr_ai.messages
|
|
2555
|
+
SET status = 'error', content = ''
|
|
2556
|
+
WHERE id = ${assistantMsgId}
|
|
2557
|
+
`;
|
|
2558
|
+
} catch (dbErr) {
|
|
2559
|
+
console.error("[brokr] Failed to mark message as error:", dbErr);
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
} finally {
|
|
2563
|
+
generationDone = true;
|
|
2564
|
+
notifyChunk();
|
|
2565
|
+
}
|
|
2566
|
+
})();
|
|
2567
|
+
void backgroundTask(generationTask);
|
|
2568
|
+
const encoder = new TextEncoder();
|
|
2569
|
+
const stream = new ReadableStream({
|
|
2570
|
+
async pull(controller) {
|
|
2571
|
+
const enqueue = (event) => {
|
|
2572
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}
|
|
2573
|
+
|
|
2574
|
+
`));
|
|
2575
|
+
};
|
|
2576
|
+
await waitForChunk();
|
|
2577
|
+
if (!metadataSent && opts.persist && threadId) {
|
|
2578
|
+
metadataSent = true;
|
|
2579
|
+
enqueue({
|
|
2580
|
+
type: "conversation",
|
|
2581
|
+
id: threadId,
|
|
2582
|
+
assistantMessageId: assistantMsgId,
|
|
2583
|
+
isNewThread
|
|
2584
|
+
});
|
|
2585
|
+
}
|
|
2586
|
+
while (chunkIndex < chunks.length) {
|
|
2587
|
+
enqueue({ type: "delta", delta: chunks[chunkIndex] });
|
|
2588
|
+
chunkIndex++;
|
|
2589
|
+
}
|
|
2590
|
+
if (generationDone || generationError) {
|
|
2591
|
+
if (generationError) {
|
|
2592
|
+
enqueue({ type: "error", message: generationError });
|
|
2593
|
+
} else {
|
|
2594
|
+
enqueue({ type: "done" });
|
|
2595
|
+
}
|
|
2596
|
+
controller.close();
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
});
|
|
2600
|
+
return new Response(stream, {
|
|
2601
|
+
headers: {
|
|
2602
|
+
"Content-Type": "text/event-stream",
|
|
2603
|
+
"Cache-Control": "no-cache",
|
|
2604
|
+
Connection: "keep-alive"
|
|
2605
|
+
}
|
|
2606
|
+
});
|
|
2607
|
+
}
|
|
2608
|
+
async function handleListNotifications(request, opts) {
|
|
2609
|
+
const url = new URL(request.url);
|
|
2610
|
+
const userId = url.searchParams.get("userId");
|
|
2611
|
+
if (!userId) {
|
|
2612
|
+
return Response.json({ error: "userId is required" }, { status: 400 });
|
|
2613
|
+
}
|
|
2614
|
+
const tableName = opts.notifications?.tableName;
|
|
2615
|
+
try {
|
|
2616
|
+
const notifications = await listPersistedNotifications(
|
|
2617
|
+
userId,
|
|
2618
|
+
{
|
|
2619
|
+
limit: Math.min(Number(url.searchParams.get("limit")) || 50, 100),
|
|
2620
|
+
unreadOnly: url.searchParams.get("unreadOnly") === "true",
|
|
2621
|
+
type: url.searchParams.get("type") ?? void 0
|
|
2622
|
+
},
|
|
2623
|
+
{ tableName }
|
|
2624
|
+
);
|
|
2625
|
+
return Response.json({ notifications });
|
|
2626
|
+
} catch (err) {
|
|
2627
|
+
console.error("[brokr] List notifications failed:", err);
|
|
2628
|
+
return Response.json({ notifications: [] });
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
async function handleMarkNotificationRead(request, opts) {
|
|
2632
|
+
const body = await request.json();
|
|
2633
|
+
if (!body.notificationId || !body.userId) {
|
|
2634
|
+
return Response.json({ error: "notificationId and userId are required" }, { status: 400 });
|
|
2635
|
+
}
|
|
2636
|
+
const tableName = opts.notifications?.tableName;
|
|
2637
|
+
try {
|
|
2638
|
+
await markNotificationRead(body.notificationId, body.userId, { tableName });
|
|
2639
|
+
return Response.json({ success: true });
|
|
2640
|
+
} catch (err) {
|
|
2641
|
+
console.error("[brokr] Mark notification read failed:", err);
|
|
2642
|
+
return Response.json({ error: "Failed to mark as read" }, { status: 500 });
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
function handleWsToken() {
|
|
2646
|
+
const token = process.env.BROKR_TOKEN;
|
|
2647
|
+
if (!token) {
|
|
2648
|
+
return Response.json(
|
|
2649
|
+
{ error: "BROKR_TOKEN not configured" },
|
|
2650
|
+
{ status: 500 }
|
|
2651
|
+
);
|
|
2652
|
+
}
|
|
2653
|
+
return Response.json({ token });
|
|
2654
|
+
}
|
|
2655
|
+
function createBrokrHandlers(options) {
|
|
2656
|
+
const opts = { persist: false, ...options };
|
|
2657
|
+
function withLogging(method, handler) {
|
|
2658
|
+
return async (request) => {
|
|
2659
|
+
const start = Date.now();
|
|
2660
|
+
const urlPath = new URL(request.url).pathname;
|
|
2661
|
+
try {
|
|
2662
|
+
const response = await handler(request);
|
|
2663
|
+
const dur = Date.now() - start;
|
|
2664
|
+
captureRequest(method, urlPath, response.status, dur);
|
|
2665
|
+
return response;
|
|
2666
|
+
} catch (err) {
|
|
2667
|
+
const dur = Date.now() - start;
|
|
2668
|
+
capture("error", `${method} ${urlPath} FAILED ${dur}ms \u2014 ${err instanceof Error ? err.message : "unknown"}`, urlPath);
|
|
2669
|
+
throw err;
|
|
2670
|
+
}
|
|
2671
|
+
};
|
|
2672
|
+
}
|
|
2673
|
+
return {
|
|
2674
|
+
GET: withLogging("GET", async (request) => {
|
|
2675
|
+
const path = parsePath(request.url);
|
|
2676
|
+
if (path.action === "threads") return handleListThreads(request, opts);
|
|
2677
|
+
if (path.action === "messages" && path.id) return handleGetMessages(request, path.id, opts);
|
|
2678
|
+
if (path.action === "ws-token") return handleWsToken();
|
|
2679
|
+
if (path.action === "notifications") return handleListNotifications(request, opts);
|
|
2680
|
+
return new Response("Not found", { status: 404 });
|
|
2681
|
+
}),
|
|
2682
|
+
POST: withLogging("POST", async (request) => {
|
|
2683
|
+
const path = parsePath(request.url);
|
|
2684
|
+
if (path.action === "chat") return handleChat(request, opts);
|
|
2685
|
+
if (path.action === "notifications-read") return handleMarkNotificationRead(request, opts);
|
|
2686
|
+
return new Response("Not found", { status: 404 });
|
|
2687
|
+
}),
|
|
2688
|
+
PATCH: withLogging("PATCH", async (request) => {
|
|
2689
|
+
const path = parsePath(request.url);
|
|
2690
|
+
if (path.action === "rename" && path.id) return handleRenameThread(request, path.id, opts);
|
|
2691
|
+
if (path.action === "thread" && path.id) return handleRenameThread(request, path.id, opts);
|
|
2692
|
+
return new Response("Not found", { status: 404 });
|
|
2693
|
+
}),
|
|
2694
|
+
DELETE: withLogging("DELETE", async (request) => {
|
|
2695
|
+
const path = parsePath(request.url);
|
|
2696
|
+
if (path.action === "thread" && path.id) return handleDeleteThread(request, path.id, opts);
|
|
2697
|
+
return new Response("Not found", { status: 404 });
|
|
2698
|
+
})
|
|
2699
|
+
};
|
|
2700
|
+
}
|
|
2701
|
+
export {
|
|
2702
|
+
createBrokrHandlers,
|
|
2703
|
+
ensureNotificationTable,
|
|
2704
|
+
listPersistedNotifications,
|
|
2705
|
+
markNotificationRead,
|
|
2706
|
+
persistNotification,
|
|
2707
|
+
protectApi,
|
|
2708
|
+
protectPage,
|
|
2709
|
+
withUser
|
|
2710
|
+
};
|