@buildbase/sdk 0.0.48 → 0.0.49

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/AGENTS.md ADDED
@@ -0,0 +1,119 @@
1
+ # AGENTS.md — `@buildbase/sdk`
2
+
3
+ Machine-friendly reference for AI coding agents working **with** this SDK (in a consumer app) or **on** this repo. Dense by design. For prose, examples, and props tables see [README.md](./README.md); for internals see [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md) and [docs/ERROR_CODES.md](./docs/ERROR_CODES.md).
4
+
5
+ BuildBase is a multi-tenant SaaS platform SDK: auth, workspaces, subscriptions/billing (Stripe), quotas/usage, credits, feature flags, RBAC, notifications, i18n, and server-side agent (OAuth2 / discovery) tooling.
6
+
7
+ ## The one rule: two entry points, pick by runtime
8
+
9
+ | Import | Runtime | Contains |
10
+ | ----------------------- | ---------------- | ----------------------------------------------------------------------- |
11
+ | `@buildbase/sdk` | **Server** / any | Zero React. `BuildBase()` factory, API classes, webhooks, agent tooling, pure utils, enums, types. |
12
+ | `@buildbase/sdk/react` | **Client** | Everything in core **re-exported** + React providers, hooks, gate components. |
13
+ | `@buildbase/sdk/data` | any | Static datasets: `countries`, `currencies`, `languages`, `timezones`, `betaFormSchema`. |
14
+ | `@buildbase/sdk/css` | build | `import '@buildbase/sdk/css'` — required once for component styling. |
15
+
16
+ - **In a React component / client hook → import from `@buildbase/sdk/react`.** It contains the whole core surface too, so you rarely need both.
17
+ - **In an API route, server action, webhook, cron, or Node/edge → import from `@buildbase/sdk`.** Never import `/react` on the server.
18
+ - Default export of both `@buildbase/sdk` and `@buildbase/sdk/react` is the `BuildBase` server factory.
19
+ - ESM + CJS dual-published; peer dep React 18 or 19; Node ≥ 18.
20
+
21
+ ## Client setup (React)
22
+
23
+ ```tsx
24
+ import '@buildbase/sdk/css';
25
+ import { SaaSOSProvider } from '@buildbase/sdk/react';
26
+
27
+ <SaaSOSProvider serverUrl={process.env.NEXT_PUBLIC_BUILDBASE_URL!} orgId={process.env.NEXT_PUBLIC_BUILDBASE_ORG_ID!} locale="en">
28
+ {children}
29
+ </SaaSOSProvider>
30
+ ```
31
+
32
+ Everything below `SaaSOSProvider` can use the hooks and gate components. **Prefer SDK hooks over reaching into internal context/Redux.**
33
+
34
+ ## Server setup
35
+
36
+ ```ts
37
+ import BuildBase from '@buildbase/sdk';
38
+ export const { auth, workspace, subscription, usage, credits, plans, invoices,
39
+ users, features, settings, notification, permissions, withSession, client } = BuildBase({
40
+ serverUrl: process.env.BUILDBASE_URL!,
41
+ orgId: process.env.BUILDBASE_ORG_ID!,
42
+ getSessionId: async () => (await cookies()).get('bb-session-id')?.value ?? null, // Next.js
43
+ });
44
+ ```
45
+
46
+ Express / no cookie context: omit `getSessionId`, call `withSession(sessionId)` per request.
47
+ Config also accepts: `timeout`, `maxRetries`, `debug`, `headers`, `onError(err, ctx)`, `fetch`.
48
+
49
+ ## Export map (what lives where)
50
+
51
+ ### `@buildbase/sdk` (core / server) — non-React
52
+
53
+ - **Factory:** `BuildBase` (also default export) → `{ auth, workspace, subscription, usage, credits, plans, invoices, users, features, settings, notification, permissions, withSession, client }`.
54
+ - **API classes:** `BaseApi` (extend for custom APIs), `AuthApi`, `UserApi`, `WorkspaceApi`, `SettingsApi`, `PushApi`.
55
+ - **Webhook verification (Node only):** `verifyWebhookSignature`, `parseWebhookEvent`. Verify against the **raw** request body (`await req.text()`), not the parsed JSON.
56
+ - **Agent readiness (framework-agnostic, returns plain `DiscoveryDocument`):** `resolveWellKnown` (dispatch all `.well-known/*`), `fetchAgentReadiness` (fail-soft, cached), `clearAgentReadinessCache`, `buildAgentCard`, `buildProtectedResourceMetadata`, `buildAgentSkillsIndex`, `buildSkillMd`, `buildSecurityTxt`, `buildLlmsTxt`, `sha256Digest`. Types: `AgentReadyConfig`, `AgentReadinessBundle`, `AgentSkill`, `DiscoveryDocument`.
57
+ - **OAuth2 app-bridge:** `handleAppTokenRequest`, `handleAppRevokeRequest` (one-call handlers), `bearerChallenge` (RFC 9728/6750 401), `verifyAppTokenRequest`, `verifyAppRevokeRequest`, `verifyClientJwt`, `extractBearerToken`, `appTokenSuccess`, `appTokenFailure`, `AppBridgeError`. Types: `AppTokenRequestClaims`, `AppRevokeRequestClaims`, `AppTokenResult`, `AppTokenResponseBody`, `HandlerResult`.
58
+ - **Currency & pricing utils (pure):** `formatCents`, `formatOverageRate`, `formatOverageRateWithLabel`, `formatQuotaIncludedOverage`, `formatQuotaWithPrice`, `getCurrencySymbol`, `getCurrencyFlag`, `getQuotaUnitLabelFromName`, `getQuotaDisplayValue`, `CURRENCY_DISPLAY`, `CURRENCY_FLAG`, `PLAN_CURRENCY_CODES`, `PLAN_CURRENCY_OPTIONS`.
59
+ - **Pricing variants (multi-currency):** `getPricingVariant`, `getBasePriceCents`, `getStripePriceIdForInterval`, `getQuotaOverageCents`, `getQuotaDisplayWithVariant`, `getAvailableCurrenciesFromPlans`, `getDisplayCurrency`, `getBillingIntervalAndCurrencyFromPriceId`.
60
+ - **Seat pricing:** `calculateBillableSeats`, `calculateSeatOverageCents`, `calculateTotalSubscriptionCents`, `getPerSeatPriceCents`, `getSeatPricing`.
61
+ - **RBAC (pure):** `hasPermission`, `resolvePermissions`, `resolveMaxUsers`, `validateInvite`, `DEFAULT_ROLE_PERMISSIONS`, `Permission` (enum).
62
+ - **URL / auth-intent / redirects:** `BB_PARAM`, `BBAction`, `BBScreen`, `BBStatus`, `createBBUrl`, `readBBParams`, `cleanBBParams`, `createCheckoutRedirectUrls`, `createCreditPurchaseRedirectUrls`, `safeRedirect`, `validateRedirectUrl`, `saveAuthIntent`, `consumeAuthIntent`, `clearAuthIntent`.
63
+ - **Context invalidation (call after server mutations to refetch client contexts):** `invalidateSubscription`, `invalidateQuotaUsage`, `invalidateCreditBalance`.
64
+ - **Events:** `EventEmitter`, `eventEmitter` (singleton), `SDKEvent` (enum).
65
+ - **Push:** `PushApi`, `PUSH_SERVICE_WORKER_SCRIPT`.
66
+ - **Enums:** `ApiVersion`, `AuthStatus`, `BillingIntervals`, `CreditBucketSource`, `CreditBucketStatus`, `CreditTransactionType`, `DunningState`, `InvoiceStatuses`, `SubscriptionItemType`, `SubscriptionStatus`.
67
+
68
+ ### `@buildbase/sdk/react` — client-only additions (core is re-exported here too)
69
+
70
+ - **Providers:** `SaaSOSProvider` (root), `SubscriptionContextProvider`, `QuotaUsageContextProvider`, `CreditBalanceContextProvider`, `PushNotificationProvider`.
71
+ - **State hooks:** `useSaaSAuth`, `useSaaSWorkspaces`, `useSaaSOs`, `useSaaSSettings`, `useUserAttributes`, `useUserFeatures`, `useTranslation`, `usePermissions`, `useSeatStatus`, `useTrialStatus`, `useFullScreenLoader`.
72
+ - **Subscription/billing hooks:** `usePublicPlans`, `useSubscription`, `useSubscriptionContext`, `useSubscriptionManagement`, `usePlanGroup`, `usePlanGroupVersions`, `usePublicPlanGroupVersion`, `useCreateCheckoutSession`, `useUpdateSubscription`, `useCancelSubscription`, `useResumeSubscription`, `useBillingPortal`, `useInvoices`, `useInvoice`.
73
+ - **Quota hooks:** `useQuotaUsageContext`, `useRecordUsage`, `useQuotaUsageStatus`, `useAllQuotaUsage`, `useUsageLogs`.
74
+ - **Credit hooks:** `useCreditBalance`, `useCreditBalanceContext`, `useConsumeCredits`, `usePurchaseCredits`, `useCreditPackages`, `usePublicCreditPackages`, `useCreditTransactions`, `useExpiringCredits`.
75
+ - **Push hooks:** `usePushNotifications`.
76
+ - **Gate components (conditional render by state):**
77
+ - Auth: `WhenAuthenticated`, `WhenUnauthenticated`
78
+ - Roles: `WhenRoles`, `WhenWorkspaceRoles`, `WhenPermission`
79
+ - Features: `WhenUserFeatureEnabled`, `WhenUserFeatureDisabled`, `WhenWorkspaceFeatureEnabled`, `WhenWorkspaceFeatureDisabled`
80
+ - Subscription: `WhenSubscription`, `WhenNoSubscription`, `WhenSubscriptionToPlans`
81
+ - Trial: `WhenTrialing`, `WhenNotTrialing`, `WhenTrialEnding`
82
+ - Quota: `WhenQuotaAvailable`, `WhenQuotaExhausted`, `WhenQuotaOverage`, `WhenQuotaThreshold`
83
+ - Credits: `WhenCreditsAvailable`, `WhenCreditsExhausted`, `WhenCreditsLow`
84
+ - **UI components:** `WorkspaceSwitcher`, `PricingPage`, `CreditStorePage`, `CreditBalance`, `CreditActionsProvider`, `BetaForm`, `FullScreenLoader`.
85
+ - **Misc:** `SUPPORTED_LOCALES`, `SETTINGS_SCREENS`, `SettingsScreen`, `workspaceSettingsManager`.
86
+
87
+ ### `@buildbase/sdk/data`
88
+
89
+ `countries`, `currencies`, `languages`, `timezones`, `betaFormSchema` (Zod), type `BetaFormValues`.
90
+
91
+ ## Server actions cheat-sheet
92
+
93
+ `workspace`: `list get create update delete` · `users`: `list invite remove updateRole getProfile updateProfile` · `subscription`: `get checkout update cancel resume getBillingPortalUrl` · `plans`: `getGroup getVersions getPublic getVersion` · `invoices`: `list get` · `usage`: `record recordBatch getQuota getAll getLogs` · `credits`: `getBalance consume purchase getPackages getTransactions getExpiring getBuckets getPublicPackages` · `settings`: `get` · `features`: `list update` · `notification`: `send(workspaceId, event, userId?, data?)` · `permissions`: `check(workspaceId, userId, permission) resolve(workspaceId, userId)`.
94
+
95
+ ## Internationalization (i18n)
96
+
97
+ - 8 locales: `en es fr de ja zh hi ar`. English is bundled; others lazy-load. `SUPPORTED_LOCALES` lists them.
98
+ - All message catalogs are typed `: SDKMessages` (see `src/i18n/types.ts`) — **the type system guarantees every locale has the complete key set**; a missing/extra key fails `tsc`.
99
+ - Client: `const { t, locale, dir, fmtNum, fmtCents } = useTranslation()`. `t('a.b.c', { count })` is ICU MessageFormat with compile-time-checked keys. `dir` is `'rtl'` for Arabic. Hindi/Arabic render native numerals.
100
+ - Set the active locale via `SaaSOSProvider locale="…"` (accepts regional tags like `zh-TW`, resolved to the nearest supported locale).
101
+ - Only **user-facing UI strings** are translated. Server/machine output (JWT error codes, discovery JSON, webhook codes) is intentionally **not** localized.
102
+
103
+ ## Gotchas (read before you touch)
104
+
105
+ - **Server vs client imports** — importing `@buildbase/sdk/react` on the server pulls React in; keep server code on `@buildbase/sdk`.
106
+ - **Webhooks** — always `verifyWebhookSignature`/`parseWebhookEvent` against the raw body string. In Next.js App Router use `await req.text()`, not `req.json()`.
107
+ - **After a server-side mutation** that changes subscription/quota/credits, call the matching `invalidate*` helper so client contexts refetch.
108
+ - **Agent-readiness fetches are fail-soft** — a platform outage yields `{ enabled: false }`, never a 500. Don't add your own error handling expecting throws.
109
+ - **New crypto is dependency-free pure JS** (`src/lib/sha256.ts`) so it behaves identically on browser/edge/Node — don't swap in Web Crypto / Node `crypto` without matching outputs.
110
+ - **Prefer SDK hooks** (`useSaaS*`, `useSubscription*`, etc.) over `useAppSelector`/internal context so consumer code stays stable across internal refactors.
111
+
112
+ ## Working on this repo
113
+
114
+ - `npm run build` — rollup build of all entry points into `dist/` (also runs on `prepublishOnly`).
115
+ - `npx tsc --noEmit` — typecheck (also the i18n completeness guarantee).
116
+ - `npm run format` — Prettier + organize-imports.
117
+ - `npm run dev` / `npm run watch:push` — watch build (+ `yalc push` for local consumers).
118
+ - No test runner is configured; verify changes via `tsc`, `build`, and a consumer app.
119
+ - Source layout: `src/api` (API clients + pure billing utils), `src/contexts` + `src/providers` (React state), `src/hooks`, `src/components` (incl. `ui/` primitives), `src/lib` (framework-agnostic utils, webhooks, agent tooling, crypto), `src/i18n` (catalogs + `useTranslation`).
package/CHANGELOG.md ADDED
@@ -0,0 +1,42 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.0.49] - 2026-07-07
9
+
10
+ ### Added
11
+
12
+ - **Server-side agent-readiness discovery** (`@buildbase/sdk`): framework-agnostic helpers to make a consuming app "agent ready" — `resolveWellKnown`, `fetchAgentReadiness`, `clearAgentReadinessCache`, `buildAgentCard`, `buildProtectedResourceMetadata`, `buildAgentSkillsIndex`, `buildSkillMd`, `buildSecurityTxt`, `buildLlmsTxt`, `sha256Digest`; types `AgentReadyConfig`, `AgentReadinessBundle`, `AgentSkill`, `DiscoveryDocument`. Serves Agent Card, OAuth protected-resource metadata (RFC 9728), Agent Skills, `security.txt` (RFC 9116), and `llms.txt`. Fetches are fail-soft and cached in-memory.
13
+ - **Server-side OAuth2 app-bridge** (`@buildbase/sdk`): verify BuildBase's signed `applicationTokenUrl`/`applicationRevokeUrl` webhook requests and shape the exact response the platform expects — `handleAppTokenRequest`, `handleAppRevokeRequest`, `bearerChallenge`, `verifyAppTokenRequest`, `verifyAppRevokeRequest`, `verifyClientJwt`, `extractBearerToken`, `appTokenSuccess`, `appTokenFailure`, `AppBridgeError`; types `AppTokenRequestClaims`, `AppRevokeRequestClaims`, `AppTokenResult`, `AppTokenResponseBody`, `HandlerResult`. HS256 JWT verification is timing-safe with no `alg` confusion.
14
+ - **Dependency-free SHA-256 / HMAC-SHA256** (`src/lib/sha256.ts`): pure-JS implementation (no Web Crypto, no Node `crypto`) shared by the discovery and app-bridge toolkits, so digests are identical on every runtime (browser, edge, Node 18+).
15
+ - **README**: new "Webhook Verification", "Agent Readiness (Discovery)", and "OAuth2 App Bridge" sections, plus server-only toolkits in the API Reference.
16
+
17
+ ### Fixed
18
+
19
+ - **Workspace → Users crash**: a member whose `name` is null/undefined (common with OAuth/passkey signups) no longer crashes the entire member list; `getUserDisplay` now falls back to the "unknown user" label.
20
+ - **Duplicate `onError` callbacks**: network/timeout failures routed through `fetchJson` invoked the `onError` callback twice (double Sentry capture / double error toast). The callback now fires at most once per failure, regardless of code path.
21
+ - Corrected the `agent-discovery` module JSDoc example, which referenced a non-existent `agentReady()` factory; it now uses the actual `resolveWellKnown` / `buildLlmsTxt` API.
22
+ - **AI-agent docs**: added `AGENTS.md` (shipped in the package) — a dense reference of both entry points, the full export map, the i18n contract, and gotchas.
23
+
24
+ ## [0.0.19] - 2025-02-07
25
+
26
+ ### Added
27
+
28
+ - **Multi-currency support**: New `currency-utils` with `CURRENCY_DISPLAY`, `CURRENCY_FLAG`, `PLAN_CURRENCY_CODES`, `PLAN_CURRENCY_OPTIONS`, `formatCents`, `formatOverageRate`, `formatOverageRateWithLabel`, `formatQuotaIncludedOverage`, `getCurrencyFlag`, `getCurrencySymbol`, `getQuotaUnitLabelFromName`.
29
+ - **Pricing variant utilities**: New `pricing-variant-utils` with `getPricingVariant`, `getBasePriceCents`, `getStripePriceIdForInterval`, `getQuotaOverageCents`, `getQuotaDisplayWithVariant`, `getAvailableCurrenciesFromPlans`, `getDisplayCurrency`, `getBillingIntervalAndCurrencyFromPriceId`; types `IPricingVariant`, `PlanVersionWithPricingVariants`, `QuotaDisplayWithOverage`.
30
+ - **API types**: `IPricingVariant`, `IStripePricesByInterval`, `IQuotaOveragesByInterval`, `IQuotaOveragePriceIdsByInterval`; plan/plan-version types now support `pricingVariants` and per-interval quotas.
31
+
32
+ ### Changed
33
+
34
+ - **Plan and subscription types**: `IPlanVersion` uses `pricingVariants` (multi-currency) instead of single `basePricing`/`stripePrices`; `quotas` unified to `IQuotaByInterval`; `IQuotaIntervalValue` overage/priceId optional for public plans.
35
+ - **Public plans**: `IPublicPlanVersion` uses `pricingVariants` and `IQuotaByInterval`; removed `IPublicPlanPricing`, `IPublicPlanQuotaValue`; added `notes` to `IPublicPlansResponse`.
36
+ - **Workspace**: Refactored workspace hooks to simplify dependencies and state management; `SubscriptionDialog` and `SettingsSubscription` updated for multi-currency and new types; `SettingsGeneral` and `SettingsInvoices` adjustments.
37
+ - **Quota utils**: `quota-utils` API updates and alignment with pricing variant overage display.
38
+ - **Docs**: README added "Multi-Currency & Pricing Utilities" section and API reference for currency/pricing-variant/quota utilities; `ARCHITECTURE.md` documents API utilities and type exports; `ERROR_CODES.md` unchanged.
39
+
40
+ ### Removed
41
+
42
+ - Legacy types: `IQuotaValue`, `IPublicPlanPricing`, `IPublicPlanQuotaValue` (replaced by pricing variants and `IQuotaByInterval`).
package/README.md CHANGED
@@ -36,6 +36,9 @@ Also works server-side (Next.js API routes, Express, Hono) — see [Server-Side
36
36
  - [API Reference](#-api-reference)
37
37
  - [Best Practices](#-best-practices)
38
38
  - [Server-Side Usage](#server-side-usage)
39
+ - [Webhook Verification](#webhook-verification)
40
+ - [Agent Readiness (Discovery)](#agent-readiness-discovery)
41
+ - [OAuth2 App Bridge](#oauth2-app-bridge)
39
42
 
40
43
  ## 🚀 Features
41
44
 
@@ -2017,6 +2020,15 @@ All SDK API clients extend a shared base class and are exported from the package
2017
2020
  | `WorkspaceApi` | Workspaces, subscription, invoices, quota usage, users |
2018
2021
  | `SettingsApi` | Organization settings |
2019
2022
 
2023
+ Server-only toolkits (framework-agnostic, zero React) are exported from `@buildbase/sdk`:
2024
+
2025
+ | Export group | Purpose |
2026
+ | ------------------------ | ---------------------------------------------------------------------------------------------------------- |
2027
+ | `BuildBase()` factory | Session-scoped server actions — see [Server-Side Usage](#server-side-usage) |
2028
+ | Webhook verification | `verifyWebhookSignature`, `parseWebhookEvent` — see [Webhook Verification](#webhook-verification) |
2029
+ | Agent readiness | `resolveWellKnown`, `buildAgentCard`, `buildLlmsTxt`, … — see [Agent Readiness](#agent-readiness-discovery) |
2030
+ | OAuth2 app bridge | `handleAppTokenRequest`, `handleAppRevokeRequest`, `bearerChallenge`, … — see [OAuth2 App Bridge](#oauth2-app-bridge) |
2031
+
2020
2032
  ### Components
2021
2033
 
2022
2034
  | Component | Purpose |
@@ -2795,6 +2807,192 @@ BuildBase({
2795
2807
  })
2796
2808
  ```
2797
2809
 
2810
+ ## Webhook Verification
2811
+
2812
+ BuildBase signs every outbound webhook with HMAC-SHA256 over `<timestamp>.<rawBody>`. Verify it before trusting the payload. **Node.js only** (uses the built-in `crypto` module).
2813
+
2814
+ Always verify against the **raw** request body — parse only after verification succeeds.
2815
+
2816
+ ```ts
2817
+ import { parseWebhookEvent, verifyWebhookSignature } from '@buildbase/sdk';
2818
+
2819
+ // One-step: verify + parse (returns null when invalid)
2820
+ app.post('/webhooks/buildbase', (req, res) => {
2821
+ const event = parseWebhookEvent({
2822
+ body: req.rawBody, // raw string, NOT the JSON-parsed object
2823
+ signature: req.headers['x-buildbase-signature'], // "sha256=<hex>"
2824
+ timestamp: req.headers['x-buildbase-timestamp'],
2825
+ secret: process.env.BUILDBASE_WEBHOOK_SECRET!,
2826
+ });
2827
+
2828
+ if (!event) return res.status(401).json({ error: 'Invalid webhook' });
2829
+
2830
+ switch (event.event) {
2831
+ case 'subscription.created':
2832
+ // event.data.subscription ...
2833
+ break;
2834
+ case 'workspace.member_added':
2835
+ // event.data.targetUser ...
2836
+ break;
2837
+ }
2838
+ res.json({ received: true });
2839
+ });
2840
+ ```
2841
+
2842
+ `verifyWebhookSignature(options)` → `boolean` — use directly when you want to control parsing yourself.
2843
+
2844
+ | Option | Type | Notes |
2845
+ | --------------- | ------------------- | ----------------------------------------------------------- |
2846
+ | `body` | `string` | Raw request body, byte-for-byte as sent. |
2847
+ | `signature` | `string \| null` | `x-buildbase-signature` header (`sha256=<hex>`). |
2848
+ | `timestamp` | `string \| null` | `x-buildbase-timestamp` header. |
2849
+ | `secret` | `string` | Your endpoint's signing secret. |
2850
+ | `maxAgeSeconds` | `number` (def. 300) | Replay window. Set to `0` to skip the timestamp age check. |
2851
+
2852
+ > **Next.js App Router:** read the raw body with `await req.text()` — do not use `req.json()`, which discards the exact bytes the signature was computed over.
2853
+
2854
+ ## Agent Readiness (Discovery)
2855
+
2856
+ Make a consuming app [agent ready](https://isitagentready.com) by serving the standard machine-readable discovery documents an AI agent looks for — Agent Card, OAuth protected-resource metadata (RFC 9728), Agent Skills, `security.txt` (RFC 9116), and `llms.txt` — with the BuildBase server as the source of truth. Framework-agnostic and zero React: every function returns a plain `DiscoveryDocument` (`{ status, contentType, body, cacheControl }`); you wire it into your own router.
2857
+
2858
+ ```ts
2859
+ // lib/agent-ready.ts
2860
+ import type { AgentReadyConfig } from '@buildbase/sdk';
2861
+
2862
+ export const agentConfig: AgentReadyConfig = {
2863
+ serverUrl: process.env.BUILDBASE_URL!,
2864
+ orgId: process.env.BUILDBASE_ORG_ID!,
2865
+ siteUrl: 'https://imejis.io',
2866
+ site: { name: 'Imejis', description: 'Generate images from templates via API.' },
2867
+ // skills, security, cacheTtlSeconds are all optional
2868
+ };
2869
+ ```
2870
+
2871
+ Serve every `.well-known/*` document from one catch-all route via `resolveWellKnown`:
2872
+
2873
+ ```ts
2874
+ // app/.well-known/[...path]/route.ts (Next.js App Router)
2875
+ import { resolveWellKnown } from '@buildbase/sdk';
2876
+ import { agentConfig } from '@/lib/agent-ready';
2877
+
2878
+ export async function GET(req: Request) {
2879
+ const doc = await resolveWellKnown(new URL(req.url).pathname, agentConfig);
2880
+ if (!doc) return new Response('{"error":"not_found"}', { status: 404 });
2881
+ return new Response(doc.body, {
2882
+ status: doc.status,
2883
+ headers: { 'Content-Type': doc.contentType, 'Cache-Control': doc.cacheControl },
2884
+ });
2885
+ }
2886
+ ```
2887
+
2888
+ `resolveWellKnown` handles `agent.json`, `oauth-protected-resource` (+ per-resource paths), the `oauth-authorization-server` pointer, the Agent Skills index + `SKILL.md`, and `security.txt`. Serve `llms.txt` from its own route (it lives at the site root, not under `.well-known`):
2889
+
2890
+ ```ts
2891
+ // app/llms.txt/route.ts
2892
+ import { buildLlmsTxt, fetchAgentReadiness } from '@buildbase/sdk';
2893
+ import { agentConfig } from '@/lib/agent-ready';
2894
+
2895
+ export async function GET() {
2896
+ const doc = buildLlmsTxt(agentConfig, await fetchAgentReadiness(agentConfig));
2897
+ if (!doc) return new Response('Not found', { status: 404 });
2898
+ return new Response(doc.body, { headers: { 'Content-Type': doc.contentType } });
2899
+ }
2900
+ ```
2901
+
2902
+ `fetchAgentReadiness` is **fail-soft** — any network/HTTP/parse error resolves to `{ enabled: false }`, so a discovery route never 500s and never leaks that the platform is down; agents simply see the app as not (yet) agent-ready. Results are cached in-memory for `cacheTtlSeconds` (default 300s); call `clearAgentReadinessCache()` to reset (mainly for tests).
2903
+
2904
+ **Functions:** `resolveWellKnown`, `fetchAgentReadiness`, `clearAgentReadinessCache`, `buildAgentCard`, `buildProtectedResourceMetadata`, `buildAgentSkillsIndex`, `buildSkillMd`, `buildSecurityTxt`, `buildLlmsTxt`, `sha256Digest`. **Types:** `AgentReadyConfig`, `AgentReadinessBundle`, `AgentSkill`, `DiscoveryDocument`.
2905
+
2906
+ ## OAuth2 App Bridge
2907
+
2908
+ BuildBase runs the full OAuth2 authorization flow (login, consent, code, PKCE, refresh rotation) but never mints the access token itself — on the token grant it calls **your** backend to mint the token the agent will carry. It makes signed webhook-style calls to two endpoints your OAuth2 client registers:
2909
+
2910
+ - `applicationTokenUrl` — mint an access token for a user (**required**)
2911
+ - `applicationRevokeUrl` — invalidate a user's token on revocation (optional)
2912
+
2913
+ Both requests carry `Authorization: Bearer <JWT>`, an HS256 JWT signed with your client secret. This toolkit verifies those requests (timing-safe, no `alg` confusion) and shapes the exact response body the platform expects, so you write only the part that's yours: minting/invalidating a token in your own format.
2914
+
2915
+ ```ts
2916
+ // applicationTokenUrl handler (Next.js Pages Router)
2917
+ import { handleAppTokenRequest } from '@buildbase/sdk';
2918
+
2919
+ export default async function handler(req, res) {
2920
+ const { status, body } = await handleAppTokenRequest({
2921
+ authorization: req.headers.authorization,
2922
+ clientSecret: process.env.BUILDBASE_CLIENT_SECRET!,
2923
+ mintToken: (user) => ({
2924
+ token: signMyAccessToken(user, { aud: user.resource }), // your token, your format
2925
+ expiresIn: 3600,
2926
+ }),
2927
+ });
2928
+ res.status(status).json(body); // 401 + failure body when verification fails
2929
+ }
2930
+ ```
2931
+
2932
+ ```ts
2933
+ // applicationRevokeUrl handler
2934
+ import { handleAppRevokeRequest } from '@buildbase/sdk';
2935
+
2936
+ export default async function handler(req, res) {
2937
+ const { status, body } = await handleAppRevokeRequest({
2938
+ authorization: req.headers.authorization,
2939
+ clientSecret: process.env.BUILDBASE_CLIENT_SECRET!,
2940
+ onRevoke: async ({ userId, clientId, reason }) => {
2941
+ await revokeMyTokensFor(userId, clientId);
2942
+ },
2943
+ });
2944
+ res.status(status).json(body);
2945
+ }
2946
+ ```
2947
+
2948
+ When an agent calls your protected API without a valid token, reply with an RFC 9728 / RFC 6750 challenge that points it at your protected-resource metadata, bootstrapping OAuth discovery:
2949
+
2950
+ ```ts
2951
+ import { bearerChallenge } from '@buildbase/sdk';
2952
+
2953
+ const c = bearerChallenge({
2954
+ resourceMetadataUrl: 'https://imejis.io/.well-known/oauth-protected-resource',
2955
+ error: 'invalid_token',
2956
+ });
2957
+ res.writeHead(c.status, c.headers).end(c.body);
2958
+ ```
2959
+
2960
+ ### Serving multiple clients
2961
+
2962
+ Each integration (an AI agent, Zapier, n8n, …) is its own BuildBase OAuth2 client with its own id/secret, but they can all share one set of endpoints: BuildBase sends the caller's `clientId` as a query param, so verify each request with the secret that matches it.
2963
+
2964
+ ```ts
2965
+ const SECRETS: Record<string, string> = {
2966
+ [process.env.AGENT_CLIENT_ID!]: process.env.AGENT_CLIENT_SECRET!,
2967
+ [process.env.ZAPIER_CLIENT_ID!]: process.env.ZAPIER_CLIENT_SECRET!,
2968
+ };
2969
+
2970
+ const clientSecret = SECRETS[String(req.query.clientId)];
2971
+ if (!clientSecret) {
2972
+ return res.status(401).json({ success: false, token: '', message: 'unknown_client' });
2973
+ }
2974
+ const { status, body } = await handleAppTokenRequest({ authorization: req.headers.authorization, clientSecret, mintToken });
2975
+ ```
2976
+
2977
+ Adding a new integration is then just: register it in BuildBase, point its `applicationTokenUrl` at the same endpoint, and add its `id → secret` to the map. Selecting the secret by `clientId` is safe — the `clientId` is public and only *picks* the key; the HS256 signature check is still the real gate.
2978
+
2979
+ ### `applicationProfileUrl` (userinfo)
2980
+
2981
+ If your client also registers an `applicationProfileUrl`, treat it as a userinfo / token-validation endpoint: validate the access token **you** minted (with your own verifier — *not* the client secret) and return the profile.
2982
+
2983
+ ```ts
2984
+ import { extractBearerToken } from '@buildbase/sdk';
2985
+
2986
+ const token = extractBearerToken(req.headers.authorization);
2987
+ const claims = verifyMyAccessToken(token); // your token, your key
2988
+ if (!claims) return res.status(401).json({ error: 'invalid_token' });
2989
+ return res.json({ id: claims.id, email: claims.email /* … */ });
2990
+ ```
2991
+
2992
+ The one-call handlers cover the common path; the underlying pieces are exported for custom flows: `verifyAppTokenRequest` / `verifyAppRevokeRequest` (verify → claims), `verifyClientJwt`, `extractBearerToken`, and the response builders `appTokenSuccess` / `appTokenFailure`. Verification throws `AppBridgeError` (with a machine-readable `code` like `invalid_signature`, `token_expired`, `invalid_algorithm`). HMAC verification uses the SDK's dependency-free pure-JS HMAC-SHA256, so it behaves identically under ESM, CJS, a bundler, or bare Node.
2993
+
2994
+ **Types:** `AppTokenRequestClaims`, `AppRevokeRequestClaims`, `AppTokenResult`, `AppTokenResponseBody`, `HandlerResult`.
2995
+
2798
2996
  ## 🤝 Contributing
2799
2997
 
2800
2998
  1. Fork the repository
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});let e=!0;function t(t,...r){e&&function(){try{const e="undefined"!=typeof globalThis?globalThis:null;return"development"===e?.process?.env?.NODE_ENV}catch{return!1}}()&&console.error(t,...r)}async function r(e){const t=await e.text();if(!t)throw new Error("Empty response body");try{return JSON.parse(t)}catch(e){throw new Error(`Invalid JSON response: ${t.substring(0,100)}${t.length>100?"...":""}`)}}async function s(e,t="Request failed"){if(!e.ok){let s=t;try{const t=await r(e);s=t.message||t.error||s}catch{s=function(e,t){const r={400:"Bad Request",401:"Unauthorized - Please check your session",403:"Forbidden - You do not have permission",404:"Resource not found",500:"Internal server error",502:"Bad Gateway",503:"Service unavailable"}[e.status]||e.statusText||t;return new Error(`${t} (${e.status}: ${r})`)}(e,t).message}throw new Error(s)}try{return await r(e)}catch(e){if(e instanceof Error&&e.message.includes("Invalid JSON"))throw e;throw new Error("Failed to parse response as JSON")}}class n extends Error{constructor(e,t,r,s){super(e),this.code=t,this.context=r,this.originalError=s,this.name="SDKError","undefined"!=typeof Error&&"captureStackTrace"in Error&&Error.captureStackTrace(this,n)}}const o=new class{constructor(){this.config={enableConsoleLogging:!0,showUserNotifications:!1}}configure(t){this.config={...this.config,...t},void 0!==t.enableConsoleLogging&&function(t){void 0!==t.enableLogging&&(e=t.enableLogging)}({enableLogging:t.enableConsoleLogging})}handleError(e,r={}){if(function(e){return e instanceof Error&&("AbortError"===e.name||"ERR_CANCELED"===e.code)}(e))return;const s=e instanceof Error?e:new n("string"==typeof e?e:"An unknown error occurred","UNKNOWN_ERROR",r),o=s instanceof n?s:new n(s.message,"SDK_ERROR",r,s);if(this.config.enableConsoleLogging&&t(`[SDK Error] ${r.component||"Unknown"}:`,{message:o.message,code:o.code,context:o.context,originalError:o.originalError,stack:o.stack}),this.config.onError)try{this.config.onError(o,r)}catch(e){!function(e,...t){console.error(e,...t)}("[SDK Error Handler] Error in custom error callback:",e)}this.config.showUserNotifications&&this.notifyUser(o,r)}notifyUser(e,t){"undefined"!=typeof window&&window.dispatchEvent&&window.dispatchEvent(new CustomEvent("sdk-error",{detail:{error:e,context:t}}))}wrapAsync(e,t){return(...r)=>e(...r).catch(e=>{throw this.handleError(e,t),e})}wrapSync(e,t){return(...r)=>{try{return e(...r)}catch(e){throw this.handleError(e,t),e}}}};function i(e,t={}){o.handleError(e,t)}function a(e){if("undefined"==typeof window)return null;try{return localStorage.getItem(e)}catch(t){return i(t,{component:"storage",action:"getStorageItem",metadata:{key:e}}),null}}function c(e){if("undefined"!=typeof window)try{localStorage.removeItem(e)}catch(t){i(t,{component:"storage",action:"removeStorageItem",metadata:{key:e}})}}function u(e,t){try{!function(e,t){if("undefined"!=typeof window)try{localStorage.setItem(e,t)}catch(t){i(t,{component:"storage",action:"setStorageItem",metadata:{key:e}})}}(e,JSON.stringify(t))}catch(t){i(t,{component:"storage",action:"setStorageJSON",metadata:{key:e}})}}const l="saas-session-id";function d(){return function(){if("undefined"==typeof window)return null;try{return a(l)}catch(e){return i(e,{component:"auth-utils",action:"getSessionId",metadata:{key:l}}),null}}()}const p=new WeakSet,h="SDK is not ready (missing serverUrl, version, or orgId)";class f{constructor(e){this.serverUrl=e.serverUrl,this.version=e.version,this.orgId=e.orgId,this.requireOrgId=e.requireOrgId??!1,this.basePath=e.basePath??"public",this._sessionId=e.sessionId,this._timeout=e.timeout??3e4,this._maxRetries=e.maxRetries??0,this._debug=e.debug??!1,this._customHeaders=e.headers??{},this._onError=e.onError,this._onUnauthorized=e.onUnauthorized,this._fetch=e.fetch??globalThis.fetch}ensureReady(){if(!this.serverUrl?.trim()||!this.version)throw new Error(h);if(this.requireOrgId&&!this.orgId?.trim())throw new Error(h)}get baseUrl(){return this.ensureReady(),`${this.serverUrl}/api/${this.version}/${this.basePath}`}getAuthHeaders(){return this._sessionId?{"x-session-id":this._sessionId}:function(){const e=d(),t={};return e&&(t["x-session-id"]=e),t}()}url(e){const t=e.startsWith("/")?e.slice(1):e;return`${this.baseUrl.endsWith("/")?this.baseUrl.slice(0,-1):this.baseUrl}/${t}`}buildHeaders(e){const t={...this._customHeaders,...this.getAuthHeaders()};return e.headers&&Object.assign(t,e.headers),null==e.body||t["Content-Type"]||(t["Content-Type"]="application/json"),t}reportError(e,t){p.has(e)||(p.add(e),this._onError?.(e,t))}async executeFetch(e,t){return this.executeFetchUrl(this.url(e),t,e)}async executeFetchUrl(e,t,r){const s=(t.method??"GET").toUpperCase(),n=r??e,o=this.buildHeaders(t),i={...t,headers:o};let a;if(this._timeout>0){const e=new AbortController;a=setTimeout(()=>e.abort(),this._timeout),i.signal=t.signal?AbortSignal.any([t.signal,e.signal]):e.signal}const c=async t=>{try{const r=this._fetch.bind(globalThis),n=await r(e,i);if(this._debug&&console.log(`[BuildBase] ${s} ${e} → ${n.status}`),401===n.status&&this._onUnauthorized&&this._onUnauthorized(),n.status>=500&&t<this._maxRetries){const e=Math.min(1e3*Math.pow(2,t),1e4);return this._debug&&console.log(`[BuildBase] Retrying in ${e}ms (attempt ${t+1}/${this._maxRetries})`),await new Promise(t=>setTimeout(t,e)),c(t+1)}return n}catch(e){if(e instanceof Error&&"AbortError"===e.name){if(void 0!==a){const e=new Error(`Request timeout after ${this._timeout}ms: ${s} ${n}`);throw this.reportError(e,{method:s,path:n}),e}throw e}if(t<this._maxRetries){const e=Math.min(1e3*Math.pow(2,t),1e4);return this._debug&&console.log(`[BuildBase] Network error, retrying in ${e}ms (attempt ${t+1}/${this._maxRetries})`),await new Promise(t=>setTimeout(t,e)),c(t+1)}throw e instanceof Error&&this.reportError(e,{method:s,path:n}),e}};try{return await c(0)}finally{void 0!==a&&clearTimeout(a)}}async fetchJson(e,t={},r="Request failed"){this.ensureReady();try{const n=await this.executeFetch(e,t);return await s(n,r)}catch(r){throw r instanceof Error&&this.reportError(r,{method:(t.method??"GET").toUpperCase(),path:e}),r}}async fetchResponse(e,t={}){return this.ensureReady(),this.executeFetch(e,t)}async fetchAbsoluteUrl(e,t={}){return this.ensureReady(),this.executeFetchUrl(e,t)}async throwResponseError(e,t){let r=t;try{r=(await e.json()).message||r}catch{r=`${t} (${e.status}: ${e.statusText})`}throw new Error(r)}async unwrapResponse(e,t){const r=await e.json();if(void 0!==r.success){if(!r.success)throw new Error(r.message||t);return r.data||r}return r}}class g extends f{constructor(e){super({...e,basePath:"beta",requireOrgId:!0})}getAuthHeaders(){return{}}async fetchConfig(){return this.fetchJson(`config?orgId=${encodeURIComponent(this.orgId)}`,{},"Failed to fetch beta form configuration")}async submitBetaUser(e){return this.fetchJson("submit",{method:"POST",body:JSON.stringify(e)},"Failed to submit beta user request")}}class w extends f{constructor(e){super({...e,requireOrgId:!0})}async getVapidPublicKey(){return this.fetchJson("push/vapid-public-key")}async subscribe(e,t){await this.fetchJson("push/subscribe",{method:"POST",body:JSON.stringify({endpoint:e.endpoint,keys:e.keys,userAgent:t})})}async unsubscribe(e){await this.fetchJson("push/unsubscribe",{method:"DELETE",body:JSON.stringify({endpoint:e})})}}class y extends f{constructor(e){super({...e,requireOrgId:!0})}async getSettings(e){return this.fetchJson(`${this.orgId}/settings`,{signal:e},"Failed to fetch settings")}}class m extends f{constructor(e){super(e)}async getAttributes(e){const t=await this.fetchResponse("users/attributes",{signal:e});t.ok||await this.throwResponseError(t,"Failed to fetch user attributes");const r=await this.unwrapResponse(t,"Failed to fetch user attributes");return r&&"object"==typeof r&&!Array.isArray(r)?r:{}}async updateAttributes(e){return this.fetchJson("users/attributes",{method:"PATCH",body:JSON.stringify({attributes:e})},"Failed to update user attributes")}async updateAttribute(e,t){return this.fetchJson(`users/attributes/${e}`,{method:"PATCH",body:JSON.stringify({value:t})},"Failed to update user attribute")}async getFeatures(e){const t=await this.fetchResponse("users/features",{signal:e});t.ok||await this.throwResponseError(t,"Failed to fetch user features");const r=await this.unwrapResponse(t,"Failed to fetch user features");return"object"==typeof r&&null!==r?r:{}}}class k extends f{constructor(e){super({...e,requireOrgId:!0})}async getWorkspaces(){return this.fetchJson("workspaces",{},"Failed to fetch workspaces")}async createWorkspace(e){return this.fetchJson("workspaces",{method:"POST",body:JSON.stringify(e)},"Failed to create workspace")}async updateWorkspace(e,t){return this.fetchJson(`workspaces/${e}`,{method:"PUT",body:JSON.stringify(t)},"Failed to update workspace")}async deleteWorkspace(e){return this.fetchJson(`workspaces/${e}`,{method:"DELETE"},"Failed to delete workspace")}async getWorkspaceUsers(e){return this.fetchJson(`workspaces/${e}/users`,{},"Failed to fetch workspace users")}async addUser(e,t){return this.fetchJson(`workspaces/${e}/users/add`,{method:"POST",body:JSON.stringify(t)},"Failed to invite member")}async removeUser(e,t){return this.fetchJson(`workspaces/${e}/users/${t}`,{method:"DELETE"},"Failed to remove user")}async updateUser(e,t,r){return this.fetchJson(`workspaces/${e}/users/${t}`,{method:"PATCH",body:JSON.stringify(r)},"Failed to update user")}async updateSettings(e){return this.fetchJson("workspaces/settings",{method:"PATCH",body:JSON.stringify(e)},"Failed to update workspace settings")}async updateWorkspacePermissions(e,t){return this.fetchJson(`workspaces/${e}/permissions`,{method:"PATCH",body:JSON.stringify({permissions:t})},"Failed to update workspace permissions")}async getFeatures(){return this.fetchJson("workspaces/features",{},"Failed to fetch features")}async updateFeature(e,t,r){return this.fetchJson(`workspaces/${e}/features`,{method:"PATCH",body:JSON.stringify({features:{[t]:r}})},"Failed to update feature")}async getWorkspace(e){return this.fetchJson(`workspaces/${e}`,{},"Failed to fetch workspace")}async getProfile(){return this.fetchJson("profile",{},"Failed to fetch profile")}async updateUserProfile(e){return this.fetchJson("profile",{method:"PATCH",body:JSON.stringify(e)},"Failed to update user profile")}async getPasskeys(){const e=await this.fetchJson("passkeys",{},"Failed to fetch passkeys");return e?.passkeys??[]}async renamePasskey(e,t){await this.fetchJson(`passkeys/${e}`,{method:"PATCH",body:JSON.stringify({name:t})},"Failed to rename passkey")}async deletePasskey(e){await this.fetchJson(`passkeys/${e}`,{method:"DELETE"},"Failed to remove passkey")}async getCurrentSubscription(e){const t=await this.fetchResponse(`workspaces/${e}/subscription`);return t.ok||await this.throwResponseError(t,"Failed to fetch subscription"),this.unwrapResponse(t,"Failed to fetch subscription")}async getPlanGroup(e){const t=await this.fetchResponse(`workspaces/${e}/subscription/plan-group`);return t.ok||await this.throwResponseError(t,"Failed to fetch plan group"),this.unwrapResponse(t,"Failed to fetch plan group")}async getPlanGroupByVersion(e,t){const r=await this.fetchResponse(`workspaces/${e}/subscription/plan-group?groupVersionId=${encodeURIComponent(t)}`);return r.ok||await this.throwResponseError(r,"Failed to fetch plan group version"),this.unwrapResponse(r,"Failed to fetch plan group version")}async getPlanGroupVersions(e){const t=await this.fetchResponse(`workspaces/${e}/subscription/plan-group/versions`);return t.ok||await this.throwResponseError(t,"Failed to fetch plan group versions"),this.unwrapResponse(t,"Failed to fetch plan group versions")}async getPublicPlans(e){if(!this.orgId)throw new Error("orgId is required for getPublicPlans");const t=await this.fetchResponse(`${this.orgId}/plans/${encodeURIComponent(e)}`);return t.ok||await this.throwResponseError(t,"Failed to fetch plans"),this.unwrapResponse(t,"Failed to fetch plans")}async getPublicCreditPackages(){if(!this.orgId)throw new Error("orgId is required for getPublicCreditPackages");const e=await this.fetchResponse(`${this.orgId}/credit-packages`);return e.ok||await this.throwResponseError(e,"Failed to fetch credit packages"),this.unwrapResponse(e,"Failed to fetch credit packages")}async getPlanGroupVersion(e){const t=await this.fetchResponse(`plan-group-versions/${e}`);return t.ok||await this.throwResponseError(t,"Failed to fetch plan group version"),this.unwrapResponse(t,"Failed to fetch plan group version")}async createCheckoutSession(e,t){const r=await this.fetchResponse(`workspaces/${e}/subscription/checkout`,{method:"POST",body:JSON.stringify(t)});return r.ok||await this.throwResponseError(r,"Failed to create checkout session"),this.unwrapResponse(r,"Failed to create checkout session")}async selectFreePlan(e,t){const r=await this.fetchResponse(`workspaces/${e}/subscription/select-free-plan`,{method:"POST",body:JSON.stringify({planVersionId:t})});return r.ok||await this.throwResponseError(r,"Failed to select free plan"),r.json()}async updateSubscription(e,t){const r=await this.fetchResponse(`workspaces/${e}/subscription`,{method:"PATCH",body:JSON.stringify(t)});return r.ok||await this.throwResponseError(r,"Failed to update subscription"),this.unwrapResponse(r,"Failed to update subscription")}async createBillingPortalSession(e,t){return this.fetchJson(`workspaces/${e}/subscription/billing-portal`,{method:"POST",body:JSON.stringify(t?{returnUrl:t}:{})})}async listInvoices(e,t=10,r){const s=new URLSearchParams;s.append("limit",t.toString()),r&&s.append("starting_after",r);const n=await this.fetchResponse(`workspaces/${e}/subscription/invoices?${s.toString()}`);return n.ok||await this.throwResponseError(n,"Failed to fetch invoices"),this.unwrapResponse(n,"Failed to fetch invoices")}async getInvoice(e,t){const r=await this.fetchResponse(`workspaces/${e}/subscription/invoices/${t}`);return r.ok||await this.throwResponseError(r,"Failed to fetch invoice"),this.unwrapResponse(r,"Failed to fetch invoice")}async cancelSubscriptionAtPeriodEnd(e){const t=await this.fetchResponse(`workspaces/${e}/subscription/cancel-at-period-end`,{method:"POST"});return t.ok||await this.throwResponseError(t,"Failed to cancel subscription"),this.unwrapResponse(t,"Failed to cancel subscription")}async resumeSubscription(e){const t=await this.fetchResponse(`workspaces/${e}/subscription/resume`,{method:"POST"});return t.ok||await this.throwResponseError(t,"Failed to resume subscription"),this.unwrapResponse(t,"Failed to resume subscription")}async recordUsage(e,t){const r=await this.fetchResponse(`workspaces/${e}/subscription/usage`,{method:"POST",body:JSON.stringify(t)});return r.ok||await this.throwResponseError(r,"Failed to record usage"),this.unwrapResponse(r,"Failed to record usage")}async recordUsageBatch(e,t){const r=await this.fetchResponse(`workspaces/${e}/subscription/usage/batch`,{method:"POST",body:JSON.stringify(t)});return r.ok||await this.throwResponseError(r,"Failed to record batch usage"),this.unwrapResponse(r,"Failed to record batch usage")}async getQuotaUsageStatus(e,t){const r=await this.fetchResponse(`workspaces/${e}/subscription/usage/status?quotaSlug=${encodeURIComponent(t)}`);return r.ok||await this.throwResponseError(r,"Failed to fetch quota usage status"),this.unwrapResponse(r,"Failed to fetch quota usage status")}async getAllQuotaUsage(e){const t=await this.fetchResponse(`workspaces/${e}/subscription/usage/all`);return t.ok||await this.throwResponseError(t,"Failed to fetch all quota usage"),this.unwrapResponse(t,"Failed to fetch all quota usage")}async getUsageLogs(e,t){const r=new URLSearchParams;t?.quotaSlug&&r.append("quotaSlug",t.quotaSlug),t?.from&&r.append("from",t.from),t?.to&&r.append("to",t.to),t?.source&&r.append("source",t.source),t?.page&&r.append("page",t.page.toString()),t?.limit&&r.append("limit",t.limit.toString());const s=r.toString(),n=`workspaces/${e}/subscription/usage/logs${s?`?${s}`:""}`,o=await this.fetchResponse(n);return o.ok||await this.throwResponseError(o,"Failed to fetch usage logs"),this.unwrapResponse(o,"Failed to fetch usage logs")}async sendNotification(e,t,r,s){const n={event:t};return r&&(n.userId=r),s&&(n.data=s),this.fetchJson(`workspaces/${e}/notifications/send`,{method:"POST",body:JSON.stringify(n)},"Failed to send notification")}async getNotificationEvents(e){return this.fetchJson(`workspaces/${e}/notification-events`,{},"Failed to fetch notification events")}async getNotificationPreferences(e){return(await this.fetchJson(`workspaces/${e}/notification-preferences`,{},"Failed to fetch notification preferences")).notificationPreferences??{}}async updateNotificationPreferences(e,t){return(await this.fetchJson(`workspaces/${e}/notification-preferences`,{method:"PATCH",body:JSON.stringify({notificationPreferences:t})},"Failed to update notification preferences")).notificationPreferences??{}}async getCreditBalance(e){const t=await this.fetchResponse(`workspaces/${e}/credits`);return t.ok||await this.throwResponseError(t,"Failed to fetch credit balance"),this.unwrapResponse(t,"Failed to fetch credit balance")}async consumeCredits(e,t){const r=await this.fetchResponse(`workspaces/${e}/credits/consume`,{method:"POST",body:JSON.stringify(t)});if(!r.ok){if(402===r.status){const e=await r.json().catch(()=>({})),t=new Error(e.message||"Insufficient credits");throw t.available=e.available,t.requested=e.requested,t.code="INSUFFICIENT_CREDITS",t}await this.throwResponseError(r,"Failed to consume credits")}return this.unwrapResponse(r,"Failed to consume credits")}async purchaseCredits(e,t){const r=await this.fetchResponse(`workspaces/${e}/credits/purchase`,{method:"POST",body:JSON.stringify(t)});return r.ok||await this.throwResponseError(r,"Failed to create credit purchase checkout"),this.unwrapResponse(r,"Failed to create credit purchase checkout")}async getCreditPackages(e){const t=await this.fetchResponse(`workspaces/${e}/credits/packages`);t.ok||await this.throwResponseError(t,"Failed to fetch credit packages");const r=await this.unwrapResponse(t,"Failed to fetch credit packages");return r.docs??r}async getCreditTransactions(e,t){const r=new URLSearchParams;t?.type&&r.append("type",t.type),t?.page&&r.append("page",t.page.toString()),t?.limit&&r.append("limit",t.limit.toString());const s=r.toString(),n=`workspaces/${e}/credits/transactions${s?`?${s}`:""}`,o=await this.fetchResponse(n);return o.ok||await this.throwResponseError(o,"Failed to fetch credit transactions"),this.unwrapResponse(o,"Failed to fetch credit transactions")}async getCreditBuckets(e,t){const r=new URLSearchParams;t?.status&&r.append("status",t.status),t?.source&&r.append("source",t.source),t?.page&&r.append("page",t.page.toString()),t?.limit&&r.append("limit",t.limit.toString());const s=r.toString(),n=`workspaces/${e}/credits/buckets${s?`?${s}`:""}`,o=await this.fetchResponse(n);return o.ok||await this.throwResponseError(o,"Failed to fetch credit buckets"),this.unwrapResponse(o,"Failed to fetch credit buckets")}async getExpiringCredits(e,t){const r=t?`?days=${t}`:"",s=await this.fetchResponse(`workspaces/${e}/credits/expiring${r}`);return s.ok||await this.throwResponseError(s,"Failed to fetch expiring credits"),this.unwrapResponse(s,"Failed to fetch expiring credits")}}exports.ApiVersion=void 0,(exports.ApiVersion||(exports.ApiVersion={})).V1="v1";const b={UserCreated:"user:created",UserUpdated:"user:updated",WorkspaceChanged:"workspace:changed",WorkspaceUpdated:"workspace:updated",WorkspaceUserAdded:"workspace:user-added",WorkspaceUserRemoved:"workspace:user-removed",WorkspaceUserRoleChanged:"workspace:user-role-changed",WorkspaceCreated:"workspace:created",WorkspaceDeleted:"workspace:deleted"};class S{constructor(){this.callbacks=null}setCallbacks(e){this.callbacks=e}getCallbacks(){return this.callbacks}async emit(e,t){if(this.callbacks?.handleEvent)try{await this.callbacks.handleEvent(e,t)}catch(t){i(t,{component:"EventEmitter",action:"emit",metadata:{eventType:e}})}}async emitUserCreated(e){await this.emit(b.UserCreated,{user:e})}async emitUserUpdated(e,t){await this.emit(b.UserUpdated,{user:e,previousUser:t})}async emitWorkspaceChanged(e,t){await this.emit(b.WorkspaceChanged,{workspace:e,previousWorkspace:t})}async emitWorkspaceUpdated(e){await this.emit(b.WorkspaceUpdated,{workspace:e})}async emitWorkspaceUserAdded(e,t,r){await this.emit(b.WorkspaceUserAdded,{userId:e,workspace:t,role:r})}async emitWorkspaceUserRemoved(e,t,r){await this.emit(b.WorkspaceUserRemoved,{userId:e,workspace:t,role:r})}async emitWorkspaceUserRoleChanged(e,t,r,s){await this.emit(b.WorkspaceUserRoleChanged,{userId:e,workspace:t,previousRole:r,newRole:s})}async emitWorkspaceCreated(e){await this.emit(b.WorkspaceCreated,{workspace:e})}async emitWorkspaceDeleted(e){await this.emit(b.WorkspaceDeleted,{workspace:e})}}const v=new S;function E(){const e=new Set;return{subscribe:t=>(e.add(t),()=>{e.delete(t)}),invalidate(){e.forEach(e=>e())},reset(){e.clear()}}}const R=E().invalidate,x=E().invalidate,$=E().invalidate,P="bb",U={Checkout:"checkout",Billing:"billing",SelectPlan:"selectPlan",CreditPurchase:"creditPurchase",OpenCreditStore:"openCreditStore",Invite:"invite"},C={Success:"success",Cancel:"cancel",Error:"error"},I={Subscription:"subscription",Credits:"credits",Usage:"usage",Users:"users",Profile:"profile",General:"general",Features:"features",Notifications:"notifications",Permissions:"permissions",Danger:"danger"};function _(e,t){let r;try{const e=t||("undefined"!=typeof window?window.location.href:"https://localhost");r=new URL(e),r.search=""}catch{r=new URL("https://localhost")}return r.searchParams.set(P,function(e){return Object.entries(e).filter(([,e])=>null!=e&&""!==e).map(([e,t])=>`${encodeURIComponent(e)}:${encodeURIComponent(t)}`).join(",")}(e)),r.toString()}function A(e){if(!e||"string"!=typeof e)return null;try{const t=new URL(e);return"https:"===t.protocol?e:"http:"!==t.protocol||"localhost"!==t.hostname&&"127.0.0.1"!==t.hostname&&"::1"!==t.hostname?null:e}catch{return null}}const F="saas-auth-intent";function O(e){return"object"==typeof e&&null!==e&&"string"==typeof e.returnUrl&&"number"==typeof e.createdAt}const W={usd:"$",eur:"€",gbp:"£",jpy:"¥",cad:"CA$",aud:"A$",chf:"CHF",cny:"¥",hkd:"HK$",sgd:"S$",inr:"₹",mxn:"MX$",brl:"R$",nzd:"NZ$",sek:"kr",nok:"kr",dkk:"kr",pln:"zł",thb:"฿"},T={usd:"🇺🇸",eur:"🇪🇺",gbp:"🇬🇧",jpy:"🇯🇵",cad:"🇨🇦",aud:"🇦🇺",chf:"🇨🇭",cny:"🇨🇳",hkd:"🇭🇰",sgd:"🇸🇬",inr:"🇮🇳",mxn:"🇲🇽",brl:"🇧🇷",nzd:"🇳🇿",sek:"🇸🇪",nok:"🇳🇴",dkk:"🇩🇰",pln:"🇵🇱",thb:"🇹🇭"};function N(e){if(!e||!e.trim())return"";const t=e.trim().toLowerCase();return W[t]??t.toUpperCase()}const B=["usd","eur","gbp","jpy","cad","aud","chf","cny","hkd","sgd","inr","mxn","brl","nzd","sek","nok","dkk","pln","thb"],J=B.map(e=>({value:e,label:`${e.toUpperCase()} (${N(e)})`}));function L(e,t){return N(t)+(e/100).toFixed(2)}const M={unit:"unit",units:"units",included:"Included",afterThat:"after that",afterIncluded:"After included"};const q={included:"included",then:"then"};const D={Monthly:"monthly",Yearly:"yearly",Quarterly:"quarterly"};function V(e,t){const r=e.pricingVariants;if(!r?.length)return null;const s=t.toLowerCase();return r.find(e=>e.currency?.toLowerCase()===s)??null}function K(e,t,r){const s=V(e,t);if(!s?.basePricing)return null;const n=s.basePricing[r];return null!=n?n:null}function j(e,t,r,s){const n=V(e,t),o=n?.quotaOverages?.[r];if(o)return o[s]}function z(e,t){const r=V(e,t);return r?.seatPricing?.enabled?r.seatPricing:null}function G(e,t,r){const s=z(e,t);return s?.perSeat?s.perSeat[r]??null:null}function H(e,t){return Math.max(0,e-(t||0))}function Q(e,t,r,s){const n=z(e,t);if(!n)return null;return H(s,n.includedSeats)*(G(e,t,r)??0)}const Y={WORKSPACE_SETTINGS_VIEW:"workspace:settings:view",WORKSPACE_SETTINGS_EDIT:"workspace:settings:edit",WORKSPACE_DELETE:"workspace:delete",WORKSPACE_MEMBERS_VIEW:"workspace:members:view",WORKSPACE_MEMBERS_INVITE:"workspace:members:invite",WORKSPACE_MEMBERS_REMOVE:"workspace:members:remove",WORKSPACE_MEMBERS_ROLE_CHANGE:"workspace:members:role-change",WORKSPACE_FEATURES_VIEW:"workspace:features:view",WORKSPACE_FEATURES_EDIT:"workspace:features:edit",WORKSPACE_BILLING_VIEW:"workspace:billing:view",WORKSPACE_BILLING_MANAGE:"workspace:billing:manage",WORKSPACE_USAGE_VIEW:"workspace:usage:view"},Z=Object.values(Y),X={admin:Z.filter(e=>e!==Y.WORKSPACE_DELETE),member:[Y.WORKSPACE_SETTINGS_VIEW,Y.WORKSPACE_MEMBERS_VIEW,Y.WORKSPACE_FEATURES_VIEW,Y.WORKSPACE_BILLING_VIEW,Y.WORKSPACE_USAGE_VIEW],viewer:[Y.WORKSPACE_SETTINGS_VIEW,Y.WORKSPACE_MEMBERS_VIEW]};function ee(e){const t=new Set;for(const r of Object.values(e))for(const e of r)t.add(e);return Array.from(t)}function te(e){const{userId:t,workspace:r,settings:s,appPermissions:n}=e;if(!r||!t)return new Set;if(function(e,t){if(!t)return!1;const r=(s=e.createdBy)?"string"==typeof s?s:"object"==typeof s&&"_id"in s&&"string"==typeof s._id?s._id:null:null;var s;return null!==r&&r===t}(r,t)){const e=new Set(Z);if(n)for(const t of ee(n))e.add(t);if(r.permissions)for(const t of ee(r.permissions))e.add(t);if(s?.workspace?.permissions)for(const t of ee(s.workspace.permissions))e.add(t);return e}const o=e.workspaceRole??function(e,t){if(!t||!e.users||!Array.isArray(e.users))return null;const r=e.users.find(e=>"string"==typeof e?e===t:"object"==typeof e&&null!==e&&"_id"in e&&e._id===t);return r&&"object"==typeof r&&"role"in r&&"string"==typeof r.role?r.role:null}(r,t);if(!o)return new Set;const i=r.permissions,a=s?.workspace?.permissions,c=e=>e.filter(e=>e.startsWith("workspace:")),u=e=>e.filter(e=>!e.startsWith("workspace:")),l=i?.[o]?c(i[o]):[],d=a?.[o]?c(a[o]):[],p=X[o]??[];let h;if(l.length>0)h=l;else if(d.length>0)h=d;else if(p.length>0)h=p;else{h=X[s?.workspace?.defaultRole??"member"]??X.member??[]}const f=new Set(h),g=i?.[o]?u(i[o]):[],w=n?.[o]?u(n[o]):[],y=g.length>0?g:w;for(const e of y)f.add(e);return function(e,t,r){if(!r?.workspace)return;const{canInviteMembers:s}=r.workspace;(!1===s||"admin-only"===s&&"admin"!==t)&&e.delete(Y.WORKSPACE_MEMBERS_INVITE)}(f,o,s),f}function re(e,t){const r=te(t);return Array.isArray(e)?e.every(e=>r.has(e)):r.has(e)}var se;function ne(e){const{body:t,signature:r,timestamp:s,secret:n,maxAgeSeconds:o=300}=e;if(!(r&&s&&n&&t))return!1;const i="sha256=";if(!r.startsWith(i))return!1;const a=r.slice(7);if(o>0){const e=parseInt(s,10);if(isNaN(e))return!1;if(Math.abs(Math.floor(Date.now()/1e3)-e)>o)return!1}try{const e=require("crypto"),r=e.createHmac("sha256",n).update(`${s}.${t}`).digest("hex"),o=Buffer.from(a,"hex"),i=Buffer.from(r,"hex");return o.length===i.length&&e.timingSafeEqual(o,i)}catch{return!1}}exports.AuthStatus=void 0,(se=exports.AuthStatus||(exports.AuthStatus={})).loading="loading",se.redirecting="redirecting",se.authenticated="authenticated",se.unauthenticated="unauthenticated",se.authenticating="authenticating";const oe=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),ie=(e,t)=>e>>>t|e<<32-t;function ae(e){let t=1779033703,r=3144134277,s=1013904242,n=2773480762,o=1359893119,i=2600822924,a=528734635,c=1541459225;const u=e.length,l=u+1,d=l+(56-l%64+64)%64+8,p=new Uint8Array(d);p.set(e,0),p[u]=128;const h=8*u,f=Math.floor(h/4294967296),g=h>>>0;p[d-8]=f>>>24&255,p[d-7]=f>>>16&255,p[d-6]=f>>>8&255,p[d-5]=255&f,p[d-4]=g>>>24&255,p[d-3]=g>>>16&255,p[d-2]=g>>>8&255,p[d-1]=255&g;const w=new Uint32Array(64);for(let e=0;e<d;e+=64){for(let t=0;t<16;t++){const r=e+4*t;w[t]=p[r]<<24|p[r+1]<<16|p[r+2]<<8|p[r+3]}for(let e=16;e<64;e++){const t=ie(w[e-15],7)^ie(w[e-15],18)^w[e-15]>>>3,r=ie(w[e-2],17)^ie(w[e-2],19)^w[e-2]>>>10;w[e]=w[e-16]+t+w[e-7]+r>>>0}let u=t,l=r,d=s,h=n,f=o,g=i,y=a,m=c;for(let e=0;e<64;e++){const t=m+(ie(f,6)^ie(f,11)^ie(f,25))+(f&g^~f&y)+oe[e]+w[e]>>>0,r=u&l^u&d^l&d;m=y,y=g,g=f,f=h+t>>>0,h=d,d=l,l=u,u=t+((ie(u,2)^ie(u,13)^ie(u,22))+r>>>0)>>>0}t=t+u>>>0,r=r+l>>>0,s=s+d>>>0,n=n+h>>>0,o=o+f>>>0,i=i+g>>>0,a=a+y>>>0,c=c+m>>>0}const y=new Uint8Array(32),m=[t,r,s,n,o,i,a,c];for(let e=0;e<8;e++)y[4*e]=m[e]>>>24&255,y[4*e+1]=m[e]>>>16&255,y[4*e+2]=m[e]>>>8&255,y[4*e+3]=255&m[e];return y}function ce(e){return(new TextEncoder).encode(e)}function ue(e,t){const r=new Uint8Array(e.length+t.length);return r.set(e,0),r.set(t,e.length),r}const le=300,de="/.well-known/oauth-protected-resource",pe="/.well-known/agent-skills",he="application/json",fe="text/plain; charset=utf-8";function ge(e){return e.replace(/\/+$/,"")}function we(e){return`public, max-age=${e}, s-maxage=${e}`}function ye(e){return"sha256:"+function(e){let t="";for(let r=0;r<e.length;r++)t+=e[r].toString(16).padStart(2,"0");return t}(ae(ce(e)))}const me=new Map;async function ke(e){const t=e.cacheTtlSeconds??le,r=`${ge(e.serverUrl)}::${e.orgId}`,s=me.get(r);if(s&&s.expiresAt>Date.now())return s.value;const n=e.fetch??fetch,o=`${ge(e.serverUrl)}/api/v1/public/${e.orgId}/agent-readiness`;let i={enabled:!1};try{const e=await n(o,{headers:{Accept:he}});if(e.ok){const t=await e.json();t&&"object"==typeof t&&(i=t)}}catch{}return me.set(r,{value:i,expiresAt:Date.now()+1e3*t}),i}function be(e,t){const r=ge(e.siteUrl),s={llms_txt:"/llms.txt",sitemap:"/sitemap.xml",robots:"/robots.txt",oauth_protected_resource:de};t.authorizationServer?.metadataUrl&&(s.oauth_authorization_server=t.authorizationServer.metadataUrl),e.skills?.length&&(s.agent_skills=`${pe}/index.json`);return $e({schema_version:"1.0",name:e.site.name,...e.site.description?{description:e.site.description}:{},url:r,...e.site.documentationUrl?{documentation_url:e.site.documentationUrl}:{},provider:e.site.provider??{name:e.site.name,url:r},capabilities:s,...e.site.contactEmail?{contact:{email:e.site.contactEmail}}:{}},e)}function Se(e,t,r){const s=r.protectedResources??[];if(!s.length)return null;const n=e.replace(/\/+$/,"")||de,o=s.find(e=>function(e){let t="/";try{t=new URL(e).pathname}catch{t=e.startsWith("/")?e:`/${e}`}const r="/"===t?"":t.replace(/\/+$/,"");return de+r}(e.resource)===n);return o?$e(o.metadata,t):n===de&&1===s.length?$e(s[0].metadata,t):null}function ve(e){const t=e.skills??[];if(!t.length)return null;const r=ge(e.siteUrl);return $e({$schema:"https://schemas.agentskills.io/discovery/0.2.0/schema.json",skills:t.map(e=>({name:e.name,type:e.type??"skill-md",description:e.description,url:`${r}${Ee(e)}`,digest:ye(e.content)}))},e)}function Ee(e){return e.path??`${pe}/${e.name}/SKILL.md`}function Re(e,t){const r=(t.skills??[]).find(t=>Ee(t)===e);return r?{status:200,contentType:"text/markdown; charset=utf-8",body:r.content,cacheControl:we(t.cacheTtlSeconds??le)}:null}function xe(e){if(!e.security)return null;const t=(Array.isArray(e.security.contact)?e.security.contact:[e.security.contact]).map(e=>`Contact: ${e}`);return e.security.expires&&t.push(`Expires: ${e.security.expires}`),e.security.policy&&t.push(`Policy: ${e.security.policy}`),{status:200,contentType:fe,body:t.join("\n")+"\n",cacheControl:we(e.cacheTtlSeconds??le)}}function $e(e,t){return{status:200,contentType:he,body:JSON.stringify(e,null,2),cacheControl:we(t.cacheTtlSeconds??le)}}class Pe extends Error{constructor(e,t){super(t),this.name="AppBridgeError",this.code=e}}function Ue(e){const t=e.replace(/-/g,"+").replace(/_/g,"/"),r=t.length%4==0?"":"=".repeat(4-t.length%4),s=atob(t+r),n=new Uint8Array(s.length);for(let e=0;e<s.length;e++)n[e]=s.charCodeAt(e);return n}function Ce(e){return JSON.parse((new TextDecoder).decode(Ue(e)))}function Ie(e,t,r=60){if(!e||!t)throw new Pe("invalid_token","Missing token or client secret");const s=e.split(".");if(3!==s.length)throw new Pe("invalid_token","Malformed JWT");const[n,o,i]=s,a=function(e,t){let r=e;r.length>64&&(r=ae(r));const s=new Uint8Array(64),n=new Uint8Array(64);for(let e=0;e<64;e++){const t=e<r.length?r[e]:0;s[e]=54^t,n[e]=92^t}const o=ae(ue(s,t));return ae(ue(n,o))}(ce(t),ce(`${n}.${o}`));let c,u,l;try{c=Ue(i)}catch{throw new Pe("invalid_signature","Unreadable JWT signature")}if(!function(e,t){if(e.length!==t.length)return!1;let r=0;for(let s=0;s<e.length;s++)r|=e[s]^t[s];return 0===r}(a,c))throw new Pe("invalid_signature","JWT signature verification failed");try{u=Ce(n),l=Ce(o)}catch{throw new Pe("invalid_token","Unreadable JWT segments")}if("HS256"!==u.alg)throw new Pe("invalid_algorithm",`Unexpected JWT alg: ${String(u.alg)}`);const d=Math.floor(Date.now()/1e3);if("number"==typeof l.exp&&d>l.exp+r)throw new Pe("token_expired","JWT has expired");if("number"==typeof l.nbf&&d+r<l.nbf)throw new Pe("token_not_yet_valid","JWT is not yet valid");return l}function _e(e){if(!e)return null;const t=/^Bearer\s+(.+)$/i.exec(e.trim());return t?t[1].trim():null}function Ae(e){const t=_e(e.authorization);if(!t)throw new Pe("invalid_token","Missing Bearer token");return Ie(t,e.clientSecret,e.clockToleranceSec)}function Fe(e){const t=_e(e.authorization);if(!t)throw new Pe("invalid_token","Missing Bearer token");return Ie(t,e.clientSecret,e.clockToleranceSec)}function Oe(e){return{success:!0,token:e.token,message:e.message??"ok",..."number"==typeof e.expiresIn?{expiresIn:e.expiresIn}:{}}}function We(e){return{success:!1,token:"",message:e}}function Te(e){const{serverUrl:t,orgId:r,version:s="v1",getSessionId:n,timeout:o,maxRetries:i,debug:a,headers:c,onError:u,fetch:l}=e;if(!t)throw new Error("BuildBase: serverUrl is required");if(!r)throw new Error("BuildBase: orgId is required");const d={...void 0!==o&&{timeout:o},...void 0!==i&&{maxRetries:i},...void 0!==a&&{debug:a},...c&&{headers:c},...u&&{onError:u},...l&&{fetch:l}},p=e=>{const n={serverUrl:t,version:s,orgId:r,sessionId:e,...d};return{workspace:new k(n),user:new m(n),settings:new y(n),push:new w(n)}},h=new k({serverUrl:t,version:s,orgId:r,...d}),f=e=>({workspace:{list:async()=>(await e()).workspace.getWorkspaces(),get:async t=>(await e()).workspace.getWorkspace(t),create:async t=>(await e()).workspace.createWorkspace(t),update:async(t,r)=>(await e()).workspace.updateWorkspace(t,r),delete:async t=>(await e()).workspace.deleteWorkspace(t)},users:{list:async t=>(await e()).workspace.getWorkspaceUsers(t),invite:async(t,r,s)=>(await e()).workspace.addUser(t,{email:r,role:s}),remove:async(t,r)=>(await e()).workspace.removeUser(t,r),updateRole:async(t,r,s)=>(await e()).workspace.updateUser(t,r,{role:s}),getProfile:async()=>(await e()).workspace.getProfile(),updateProfile:async t=>(await e()).workspace.updateUserProfile(t)},subscription:{get:async t=>(await e()).workspace.getCurrentSubscription(t),checkout:async(t,r)=>(await e()).workspace.createCheckoutSession(t,r),update:async(t,r)=>(await e()).workspace.updateSubscription(t,r),cancel:async t=>(await e()).workspace.cancelSubscriptionAtPeriodEnd(t),resume:async t=>(await e()).workspace.resumeSubscription(t),getBillingPortalUrl:async(t,r)=>(await e()).workspace.createBillingPortalSession(t,r)},plans:{getGroup:async t=>(await e()).workspace.getPlanGroup(t),getVersions:async t=>(await e()).workspace.getPlanGroupVersions(t),getPublic:e=>h.getPublicPlans(e),getVersion:e=>h.getPlanGroupVersion(e)},invoices:{list:async(t,r,s)=>(await e()).workspace.listInvoices(t,r,s),get:async(t,r)=>(await e()).workspace.getInvoice(t,r)},usage:{record:async(t,r)=>(await e()).workspace.recordUsage(t,r),recordBatch:async(t,r)=>(await e()).workspace.recordUsageBatch(t,r),getQuota:async(t,r)=>(await e()).workspace.getQuotaUsageStatus(t,r),getAll:async t=>(await e()).workspace.getAllQuotaUsage(t),getLogs:async(t,r)=>(await e()).workspace.getUsageLogs(t,r)},settings:{get:async()=>(await e()).settings.getSettings()},features:{list:async()=>(await e()).workspace.getFeatures(),update:async(t,r,s)=>(await e()).workspace.updateFeature(t,r,s)},notification:{send:async(t,r,s,n)=>(await e()).workspace.sendNotification(t,r,s,n)},credits:{getBalance:async t=>(await e()).workspace.getCreditBalance(t),consume:async(t,r)=>(await e()).workspace.consumeCredits(t,r),purchase:async(t,r)=>(await e()).workspace.purchaseCredits(t,r),getPackages:async t=>(await e()).workspace.getCreditPackages(t),getTransactions:async(t,r)=>(await e()).workspace.getCreditTransactions(t,r),getBuckets:async(t,r)=>(await e()).workspace.getCreditBuckets(t,r),getExpiring:async(t,r)=>(await e()).workspace.getExpiringCredits(t,r),getPublicPackages:()=>h.getPublicCreditPackages()},permissions:{check:async(t,r,s)=>{const n=await e(),[o,i,a]=await Promise.all([n.workspace.getWorkspace(t),n.settings.getSettings(),n.workspace.getWorkspaceUsers(t)]),c=a.find(e=>("string"==typeof e.user?e.user:e.user._id)===r);return re(s,{userId:r,workspaceRole:c?.role??null,workspace:o,settings:i})},resolve:async(t,r)=>{const s=await e(),[n,o,i]=await Promise.all([s.workspace.getWorkspace(t),s.settings.getSettings(),s.workspace.getWorkspaceUsers(t)]),a=i.find(e=>("string"==typeof e.user?e.user:e.user._id)===r);return te({userId:r,workspaceRole:a?.role??null,workspace:n,settings:o})}}}),g=f(async()=>p(await(async()=>{if(!n)throw new Error("BuildBase: getSessionId callback is required for authenticated calls. Pass it in BuildBase({ getSessionId: ... }) or use withSession(id) / client.forSession(id).");const e=await n();if(!e)throw new Error("BuildBase: Not authenticated (getSessionId returned null)");return e})()));return{auth:async()=>{if(!n)return null;const e=await n();return e?{sessionId:e}:null},withSession:e=>{const t=p(e);return f(async()=>t)},client:{forSession:p},...g}}exports.AppBridgeError=Pe,exports.AuthApi=class extends f{constructor(e){super(e)}async requestAuth(e){const t=`${this.serverUrl}/api/${this.version}/auth/request`,r=await this.fetchAbsoluteUrl(t,{method:"POST",body:JSON.stringify(e)});return r.ok||await this.throwResponseError(r,"Failed to initiate authentication"),r.json()}async getProfile(e,t){return this.fetchJson("profile",{headers:{"x-session-id":e},signal:t},"Failed to fetch user profile")}},exports.BBAction=U,exports.BBScreen=I,exports.BBStatus=C,exports.BB_PARAM=P,exports.BaseApi=f,exports.BetaForm=class{constructor(e){this.api=new g(e),this.orgId=e.orgId}async fetchConfig(){return this.api.fetchConfig()}async submitBetaUser(e){const t=e?.context??{},r=t?.country??("undefined"!=typeof navigator&&navigator.language?navigator.language.split("-")[1]:void 0),s=t?.language??("undefined"!=typeof navigator&&navigator.language?navigator.language.split("-")[0]:void 0),n=t?.timezone??("undefined"!=typeof Intl&&Intl.DateTimeFormat?Intl.DateTimeFormat().resolvedOptions().timeZone:void 0),o=t?.currency??("undefined"!=typeof Intl&&Intl.NumberFormat&&Intl.NumberFormat().resolvedOptions().currency?Intl.NumberFormat().resolvedOptions().currency:void 0);return this.api.submitBetaUser({orgId:this.orgId,formData:{...e,country:r,language:s,timezone:n,currency:o}})}},exports.BillingIntervals=D,exports.BuildBase=Te,exports.CURRENCY_DISPLAY=W,exports.CURRENCY_FLAG=T,exports.CreditBucketSource={PlanGrant:"plan_grant",CreditPackPurchase:"credit_pack_purchase",AdminGrant:"admin_grant",Refund:"refund"},exports.CreditBucketStatus={Pending:"pending",Active:"active",Expired:"expired",Consumed:"consumed"},exports.CreditTransactionType={PlanGrant:"plan_grant",PackPurchased:"pack_purchased",Consumed:"consumed",Expired:"expired",AdminGrant:"admin_grant",AdminRevoke:"admin_revoke",Refund:"refund"},exports.DEFAULT_ROLE_PERMISSIONS=X,exports.DunningState={None:"none",Notified:"notified",Warning:"warning",Final:"final",Suspended:"suspended"},exports.EventEmitter=S,exports.InvoiceStatuses={Draft:"draft",Open:"open",Paid:"paid",Uncollectible:"uncollectible",Void:"void"},exports.PLAN_CURRENCY_CODES=B,exports.PLAN_CURRENCY_OPTIONS=J,exports.PUSH_SERVICE_WORKER_SCRIPT="\n// BuildBase Push Notification Service Worker\n// Place this file in your app's public directory (e.g. public/push-sw.js)\n\nself.addEventListener('push', function(event) {\n if (!event.data) return;\n\n try {\n const payload = event.data.json();\n const title = payload.title || 'Notification';\n const options = {\n body: payload.body || '',\n icon: payload.icon || undefined,\n badge: payload.icon || undefined,\n data: { url: payload.url, ...(payload.data || {}) },\n tag: 'buildbase-push-' + Date.now(),\n };\n\n event.waitUntil(\n self.registration.showNotification(title, options)\n );\n } catch (e) {\n console.error('[PushSW] Failed to show notification:', e);\n }\n});\n\nself.addEventListener('notificationclick', function(event) {\n event.notification.close();\n\n const url = event.notification.data && event.notification.data.url;\n if (url) {\n event.waitUntil(\n clients.matchAll({ type: 'window', includeUncontrolled: true }).then(function(clientList) {\n for (let i = 0; i < clientList.length; i++) {\n const client = clientList[i];\n if (client.url === url && 'focus' in client) {\n return client.focus();\n }\n }\n if (clients.openWindow) {\n return clients.openWindow(url);\n }\n })\n );\n }\n});\n",exports.Permission=Y,exports.PushApi=w,exports.SDKEvent=b,exports.SettingsApi=y,exports.SubscriptionItemType={Feature:"feature",Limit:"limit",Quota:"quota"},exports.SubscriptionStatus={Active:"active",Trialing:"trialing",Canceled:"canceled",PastDue:"past_due",Paused:"paused",Incomplete:"incomplete",Unpaid:"unpaid"},exports.UserApi=m,exports.WorkspaceApi=k,exports.appTokenFailure=We,exports.appTokenSuccess=Oe,exports.bearerChallenge=function(e){const t=[`Bearer resource_metadata="${e.resourceMetadataUrl}"`];return e.error&&t.push(`error="${e.error}"`),e.errorDescription&&t.push(`error_description="${e.errorDescription}"`),{status:401,headers:{"WWW-Authenticate":t.join(", "),"Content-Type":"application/json"},body:JSON.stringify({error:e.error??"invalid_token"})}},exports.buildAgentCard=be,exports.buildAgentSkillsIndex=ve,exports.buildLlmsTxt=function(e,t){return t.llmsTxt?{status:200,contentType:fe,body:t.llmsTxt,cacheControl:we(e.cacheTtlSeconds??le)}:null},exports.buildProtectedResourceMetadata=Se,exports.buildSecurityTxt=xe,exports.buildSkillMd=Re,exports.calculateBillableSeats=H,exports.calculateSeatOverageCents=Q,exports.calculateTotalSubscriptionCents=function(e,t,r,s){const n=K(e,t,r);return null===n?null:void 0===s?n:n+(Q(e,t,r,s)??0)},exports.cleanBBParams=function(){if("undefined"!=typeof window)try{const e=new URL(window.location.href);e.searchParams.has(P)&&(e.searchParams.delete(P),window.history.replaceState({},"",e.toString()))}catch{}},exports.clearAgentReadinessCache=function(){me.clear()},exports.clearAuthIntent=function(){c(F)},exports.consumeAuthIntent=function(){const e=function(e,t){const r=a(e);if(!r)return null;try{const s=JSON.parse(r);return t&&!t(s)?(i(new Error("Storage data failed validation"),{component:"storage",action:"getStorageJSON",metadata:{key:e}}),c(e),null):s}catch(t){return i(t,{component:"storage",action:"getStorageJSON",metadata:{key:e}}),c(e),null}}(F,O);return c(F),e?Date.now()-e.createdAt>6e5?null:A(e.returnUrl):null},exports.createBBUrl=_,exports.createCheckoutRedirectUrls=function(e,t){const r=t||("undefined"!=typeof window?window.location.href:void 0);return{successUrl:_({action:U.Checkout,status:C.Success,ws:e,screen:I.Subscription},r),cancelUrl:_({action:U.Checkout,status:C.Cancel,ws:e,screen:I.Subscription},r)}},exports.createCreditPurchaseRedirectUrls=function(e,t){const r=t||("undefined"!=typeof window?window.location.href:void 0);return{successUrl:_({action:U.CreditPurchase,status:C.Success,ws:e,screen:I.Credits},r),cancelUrl:_({action:U.CreditPurchase,status:C.Cancel,ws:e,screen:I.Credits},r)}},exports.default=Te,exports.eventEmitter=v,exports.extractBearerToken=_e,exports.fetchAgentReadiness=ke,exports.formatCents=L,exports.formatOverageRate=function(e,t,r,s){if(null==e||e<0)return null;const n={...M,...s},o=null!=t&&t>=1?t:1,i=N(r),a=(e/100).toFixed(2);return 1===o?`${i}${a}/${n.unit}`:`${i}${a}/${o.toLocaleString()} ${n.units}`},exports.formatOverageRateWithLabel=function(e,t,r,s,n,o){if(null==e||e<0)return null;const i={...M,...o},a=null!=t&&t>=1?t:1,c=N(s),u=(e/100).toFixed(2);if(r){const e=n??(r.endsWith("s")?r:`${r}s`);return a>=2?`${c}${u}/${a.toLocaleString()} ${e}`:`${c}${u}/${r}`}return 1===a?`${c}${u}/${i.unit}`:`${c}${u}/${a.toLocaleString()} ${i.units}`},exports.formatQuotaIncludedOverage=function(e,t,r,s,n,o,i,a){const c={...M,...i};if(!1===a)return null!=e?`${c.included}: ${e.toLocaleString()} (${c.hardLimit??"hard limit"})`:c.hardLimit??"Hard limit";const u=o??(r.endsWith("s")?r:`${r}s`),l=null!=n&&n>=2?`${n.toLocaleString()} ${u}`:r;return null!=e&&null!=t?`${c.included}: ${e.toLocaleString()}, ${c.afterThat} ${L(t,s)}/${l}`:null!=e?`${c.included}: ${e.toLocaleString()}`:null!=t?`${c.afterIncluded}: ${L(t,s)}/${l}`:"—"},exports.formatQuotaWithPrice=function(e,t,r={}){const{overageInCents:s=!0,currency:n}=r,o=r.labels??q,i=r.currencySymbol??N(n??"");if(null==e)return"—";const{included:a,overage:c}=e,u=`${a} ${o.included}`;if(!1===e.allowOverage)return`${u} (hard limit)`;if(null==c)return u;const l=s?c/100:c,d="number"==typeof l&&Number.isFinite(l)?l.toFixed(2):String(c),p=t&&"unitSize"in e&&null!=e.unitSize&&e.unitSize>0?` / ${e.unitSize.toLocaleString()} ${t}`:t?` / ${t}`:"";return`${u}, ${o.then} ${i}${d}${p}`},exports.getAvailableCurrenciesFromPlans=function(e){const t=new Set;for(const r of e)r.pricingVariants?.forEach(e=>{e.currency&&t.add(e.currency.toLowerCase())});return Array.from(t).sort()},exports.getBasePriceCents=K,exports.getBillingIntervalAndCurrencyFromPriceId=function(e,t){if(!e)return null;for(const r of t){const t=r.pricingVariants;if(t?.length)for(const r of t){const t=r.stripePrices;if(t){if(t.monthlyPriceId===e||t.monthly===e)return{interval:"monthly",currency:r.currency};if(t.yearlyPriceId===e||t.yearly===e)return{interval:"yearly",currency:r.currency};if(t.quarterlyPriceId===e)return{interval:"quarterly",currency:r.currency}}}}return null},exports.getCurrencyFlag=function(e){return e&&e.trim()?T[e.trim().toLowerCase()]??"":""},exports.getCurrencySymbol=N,exports.getDisplayCurrency=function(e,t){const r=V(e,t);return r?r.currency:e.plan?.currency??""},exports.getPerSeatPriceCents=G,exports.getPricingVariant=V,exports.getQuotaDisplayValue=function(e,t){if(null==e)return null;const r=e[t??"monthly"];return r?{included:r.included,...void 0!==r.overage&&{overage:r.overage},...void 0!==r.unitSize&&{unitSize:r.unitSize},allowOverage:!1!==e.allowOverage}:null},exports.getQuotaDisplayWithVariant=function(e,t,r,s){const n=e.quotas?.[r];if(null==n)return null;const o=n[s];if(!o)return null;const i=j(e,t,r,s);return{included:o.included,...void 0!==o.unitSize&&{unitSize:o.unitSize},...void 0!==i&&{overage:i},allowOverage:!1!==n.allowOverage}},exports.getQuotaOverageCents=j,exports.getQuotaUnitLabelFromName=function(e,t="unit"){const r=(e||"").trim().toLowerCase();if(!r)return t;const s=r.split(/\s+/)[0]??r;return new Set(["series","species","status","analysis","basis","bus","campus","corpus","axis"]).has(s)?s:s.endsWith("ies")&&s.length>3?s.slice(0,-3)+"y":s.endsWith("ses")||s.endsWith("xes")||s.endsWith("zes")||s.endsWith("ches")||s.endsWith("shes")?s.slice(0,-2):s.endsWith("s")&&!s.endsWith("ss")&&s.length>1?s.slice(0,-1):s||t},exports.getSeatPricing=z,exports.getStripePriceIdForInterval=function(e,t,r){const s=V(e,t);if(!s?.stripePrices)return null;const n=r===D.Monthly?"monthlyPriceId":r===D.Yearly?"yearlyPriceId":"quarterlyPriceId",o=r===D.Monthly?"monthly":r===D.Yearly?"yearly":void 0;return s.stripePrices[n]??(o?s.stripePrices[o]:void 0)??null},exports.handleAppRevokeRequest=async function(e){let t;try{t=Fe(e)}catch(e){return{status:401,body:{success:!1,message:e instanceof Pe?e.code:"invalid_token"}}}return await e.onRevoke(t),{status:200,body:{success:!0}}},exports.handleAppTokenRequest=async function(e){let t;try{t=Ae(e)}catch(e){return{status:401,body:We(e instanceof Pe?e.code:"invalid_token")}}return{status:200,body:Oe(await e.mintToken(t))}},exports.hasPermission=re,exports.invalidateCreditBalance=R,exports.invalidateQuotaUsage=x,exports.invalidateSubscription=$,exports.parseWebhookEvent=function(e){if(!ne(e))return null;try{return JSON.parse(e.body)}catch{return null}},exports.readBBParams=function(){if("undefined"==typeof window)return null;try{const e=new URLSearchParams(window.location.search).get(P);if(!e)return null;const t=function(e){const t={};if(!e)return t;const r=e.split(",");for(const e of r){const r=e.indexOf(":");if(-1===r)continue;const s=decodeURIComponent(e.slice(0,r)),n=decodeURIComponent(e.slice(r+1));s&&(t[s]=n)}return t}(e);return Object.keys(t).length>0?t:null}catch{return null}},exports.resolveMaxUsers=function(e){const{planVersion:t,currency:r="usd",settingsMaxUsers:s}=e;if(t){const e=z(t,r);if(e)return{maxUsers:e.maxSeats??0,source:"seat_pricing",includedSeats:e.includedSeats??0,hasSeatPricing:!0}}return null!=s&&s>0?{maxUsers:s,source:"settings",includedSeats:0,hasSeatPricing:!1}:{maxUsers:0,source:"none",includedSeats:0,hasSeatPricing:!1}},exports.resolvePermissions=te,exports.resolveWellKnown=async function(e,t){const r=e.replace(/\/+$/,"")||e;if("/.well-known/agent.json"===r){return be(t,await ke(t))}if("/.well-known/security.txt"===r)return xe(t);if(r===`${pe}/index.json`)return ve(t);if(r.startsWith(`${pe}/`)&&r.endsWith("SKILL.md"))return Re(r,t);if("/.well-known/oauth-authorization-server"===r){const e=await ke(t),r=e.authorizationServer?.metadataUrl;return r?$e({...e.authorizationServer?.issuer?{issuer:e.authorizationServer.issuer}:{},oauth_authorization_server_metadata:r},t):null}if(r.startsWith(de)){return Se(r,t,await ke(t))}return null},exports.safeRedirect=function(e,t="/"){const r=A(e);window.location.href=r||t},exports.saveAuthIntent=function(e){A(e)&&u(F,{returnUrl:e,createdAt:Date.now()})},exports.sha256Digest=ye,exports.validateInvite=function(e){const{memberCount:t,maxUsersConfig:r,hasSubscription:s=!0,requireSubscription:n=!1}=e;if(n&&!s)return{canInvite:!1,blockReason:"no_subscription",blockMessageKey:"users.subscriptionRequired",blockMessageValues:{}};const{maxUsers:o,source:i}=r;if(o<=0)return{canInvite:!0,blockReason:null,blockMessageKey:null,blockMessageValues:null};if(t>=o){return{canInvite:!1,blockReason:{seat_pricing:"seat_limit_reached",settings:"settings_user_limit_reached"}[i]??"seat_limit_reached",blockMessageKey:{seat_pricing:"users.seatLimitMessage",settings:"users.settingsLimitMessage"}[i]??"users.memberLimitMessage",blockMessageValues:{maxUsers:o}}}return{canInvite:!0,blockReason:null,blockMessageKey:null,blockMessageValues:null}},exports.validateRedirectUrl=A,exports.verifyAppRevokeRequest=Fe,exports.verifyAppTokenRequest=Ae,exports.verifyClientJwt=Ie,exports.verifyWebhookSignature=ne;