@buildbase/sdk 0.0.45 → 0.0.46

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/README.md CHANGED
@@ -24,6 +24,8 @@ Also works server-side (Next.js API routes, Express, Hono) — see [Server-Side
24
24
  - [Multi-Currency & Pricing Utilities](#-multi-currency--pricing-utilities)
25
25
  - [Quota Usage Tracking](#-quota-usage-tracking)
26
26
  - [Quota Gates](#-quota-gates)
27
+ - [Credit System](#-credit-system)
28
+ - [Credit Gates](#-credit-gates)
27
29
  - [Beta Form Component](#-beta-form-component)
28
30
  - [Event System](#-event-system)
29
31
  - [Error Handling](#️-error-handling)
@@ -54,6 +56,7 @@ Also works server-side (Next.js API routes, Express, Hono) — see [Server-Side
54
56
  - **📝 Beta Form** - Pre-built signup/waitlist form component
55
57
  - **📡 Event System** - Subscribe to user and workspace events
56
58
  - **🛡️ Error Handling** - Centralized error handling with error boundaries
59
+ - **💳 Credit System** - Prepaid credit balances with consume, credit gate components, and built-in workspace settings for purchasing and transaction history
57
60
  - **🖥️ Server-Side SDK** - `BuildBase()` factory for API routes, background jobs, Express, Hono — zero React dependency
58
61
  - **🌐 Internationalization (i18n)** - 8 locales (en, es, fr, de, ja, zh, hi, ar), ICU MessageFormat, RTL support, native numerals
59
62
  - **🏠 Workspace Modes** - Personal (solo B2C) or Platform (multi-user B2B), configured from admin dashboard
@@ -155,7 +158,7 @@ export default function RootLayout({ children }) {
155
158
  }
156
159
  ```
157
160
 
158
- ### 4. Workspace Management
161
+ ### 4. Workspace Switcher
159
162
 
160
163
  The WorkspaceSwitcher component uses a render prop pattern, giving you full control over the UI. Configure `onWorkspaceChange` in `auth.callbacks` (SaaSOSProvider) to handle workspace switches—used when clicking "Switch to" and when restoring from storage on page refresh. The callback receives `{ workspace, user, role }` so you don't need to look up the user's role:
161
164
 
@@ -314,7 +317,7 @@ Pass affiliate/referral data to Stripe checkout sessions via the `getCheckoutStr
314
317
  version={ApiVersion.V1}
315
318
  orgId="your-org-id"
316
319
  auth={authConfig}
317
- getCheckoutStripeParams={async (request) => {
320
+ getCheckoutStripeParams={async request => {
318
321
  // Rewardful, FirstPromoter, PartnerStack — read client_reference_id
319
322
  const referralId = await getRewardfulReferralId();
320
323
 
@@ -516,7 +519,7 @@ function SubscriptionStatus() {
516
519
  | `loading` | `boolean` | True while subscription is being fetched |
517
520
  | `refetch` | `() => Promise<void>` | Manually refetch subscription (e.g. after plan change) |
518
521
 
519
- **When subscription refetches**
522
+ #### When subscription refetches
520
523
 
521
524
  - When the current workspace changes (automatic).
522
525
  - When subscription is updated via SDK (e.g. `useUpdateSubscription`, cancel, resume) — refetch is triggered automatically.
@@ -857,7 +860,7 @@ import { SaaSOSProvider } from '@buildbase/sdk/react';
857
860
  // - Per-event email/push toggles (only user-manageable custom events)
858
861
  ```
859
862
 
860
- ### Types
863
+ ### Notification Types
861
864
 
862
865
  ```ts
863
866
  import type { NotificationData, NotificationResult, NotificationEvent } from '@buildbase/sdk';
@@ -1683,6 +1686,236 @@ function QuotaDebug() {
1683
1686
  - When usage is recorded via `useRecordUsage` — refetch is triggered automatically.
1684
1687
  - When you call `refetch()` manually.
1685
1688
 
1689
+ ## 💳 Credit System
1690
+
1691
+ Manage prepaid credit balances for your workspace — purchase credit packages, consume credits for actions, and track transactions. Credit data is loaded once per workspace via `CreditBalanceContextProvider` (included in `SaaSOSProvider` by default) and refetched automatically after consuming or purchasing credits.
1692
+
1693
+ ### Credit Hooks
1694
+
1695
+ #### useCreditBalance
1696
+
1697
+ ```tsx
1698
+ import { useCreditBalance, useSaaSWorkspaces } from '@buildbase/sdk/react';
1699
+
1700
+ function CreditDisplay() {
1701
+ const { currentWorkspace } = useSaaSWorkspaces();
1702
+ const { balance, loading, error, refetch } = useCreditBalance(currentWorkspace?._id);
1703
+
1704
+ if (loading) return <Spinner />;
1705
+ if (!balance) return <p>No credit data</p>;
1706
+
1707
+ return (
1708
+ <div>
1709
+ <p>Available: {balance.available} credits</p>
1710
+ <button onClick={refetch}>Refresh</button>
1711
+ </div>
1712
+ );
1713
+ }
1714
+ ```
1715
+
1716
+ #### useConsumeCredits
1717
+
1718
+ Consume credits for an action. Throws with `code: 'INSUFFICIENT_CREDITS'` on 402 response. Automatically invalidates the credit balance cache on success.
1719
+
1720
+ ```tsx
1721
+ import { useConsumeCredits, useSaaSWorkspaces } from '@buildbase/sdk/react';
1722
+
1723
+ function GenerateButton() {
1724
+ const { currentWorkspace } = useSaaSWorkspaces();
1725
+ const { consumeCredits, loading, error } = useConsumeCredits(currentWorkspace?._id);
1726
+
1727
+ const handleGenerate = async () => {
1728
+ try {
1729
+ const result = await consumeCredits({
1730
+ quantity: 5,
1731
+ metadata: { action: 'image-generation' },
1732
+ idempotencyKey: 'gen-abc-123', // optional: prevent duplicate consumption
1733
+ });
1734
+ console.log(`Remaining: ${result.remaining}`);
1735
+ } catch (err: any) {
1736
+ if (err.code === 'INSUFFICIENT_CREDITS') {
1737
+ console.warn(`Need ${err.requested}, only ${err.available} available`);
1738
+ }
1739
+ }
1740
+ };
1741
+
1742
+ return (
1743
+ <button onClick={handleGenerate} disabled={loading}>
1744
+ Generate (5 credits)
1745
+ </button>
1746
+ );
1747
+ }
1748
+ ```
1749
+
1750
+ #### usePublicCreditPackages
1751
+
1752
+ Fetch credit packages publicly (no authentication required) — for marketing/pricing pages:
1753
+
1754
+ ```tsx
1755
+ import { usePublicCreditPackages } from '@buildbase/sdk/react';
1756
+
1757
+ function PublicCreditsPage() {
1758
+ const { packages, notes, loading } = usePublicCreditPackages();
1759
+
1760
+ if (loading) return <Spinner />;
1761
+
1762
+ return (
1763
+ <div>
1764
+ {notes && <p>{notes}</p>}
1765
+ {packages.map(pkg => (
1766
+ <div key={pkg._id}>
1767
+ {pkg.name}: {pkg.credits} credits
1768
+ </div>
1769
+ ))}
1770
+ </div>
1771
+ );
1772
+ }
1773
+ ```
1774
+
1775
+ ### Credit Components
1776
+
1777
+ #### CreditBalance
1778
+
1779
+ Render-prop component for displaying credit balance:
1780
+
1781
+ ```tsx
1782
+ import { CreditBalance } from '@buildbase/sdk/react';
1783
+
1784
+ function MyCredits() {
1785
+ return (
1786
+ <CreditBalance>
1787
+ {({ balance, loading, refetch }) => {
1788
+ if (loading) return <Spinner />;
1789
+ if (!balance) return null;
1790
+ return <p>{balance.available} credits available</p>;
1791
+ }}
1792
+ </CreditBalance>
1793
+ );
1794
+ }
1795
+ ```
1796
+
1797
+ #### CreditActionsProvider
1798
+
1799
+ Render-prop component that combines balance, packages, consume, and purchase in one:
1800
+
1801
+ ```tsx
1802
+ import { CreditActionsProvider } from '@buildbase/sdk/react';
1803
+
1804
+ function CreditDashboard() {
1805
+ return (
1806
+ <CreditActionsProvider>
1807
+ {({ balance, packages, consume, consuming, purchase, purchasing, error }) => (
1808
+ <div>
1809
+ <p>Balance: {balance?.available ?? 0} credits</p>
1810
+
1811
+ <button onClick={() => consume({ quantity: 1 })} disabled={consuming}>
1812
+ Use 1 Credit
1813
+ </button>
1814
+
1815
+ <h3>Buy More</h3>
1816
+ {packages.map(pkg => (
1817
+ <button key={pkg._id} onClick={() => purchase(pkg)} disabled={purchasing}>
1818
+ {pkg.name} — {pkg.credits} credits
1819
+ </button>
1820
+ ))}
1821
+
1822
+ {error && <p className="text-red-500">{error}</p>}
1823
+ </div>
1824
+ )}
1825
+ </CreditActionsProvider>
1826
+ );
1827
+ }
1828
+ ```
1829
+
1830
+ ### Credit Hooks Summary
1831
+
1832
+ | Hook | Purpose |
1833
+ | -------------------------------- | ---------------------------------------------- |
1834
+ | `useCreditBalance(workspaceId)` | Get current credit balance (auto-fetches) |
1835
+ | `useConsumeCredits(workspaceId)` | Consume credits (mutation) |
1836
+ | `usePublicCreditPackages()` | List public credit packages (no auth required) |
1837
+
1838
+ > **Note:** Credit purchasing, packages, transactions, and expiring credits are managed automatically by the built-in workspace settings dialog.
1839
+
1840
+ ### Server-Side Credits
1841
+
1842
+ ```ts
1843
+ import { credits } from '@/lib/buildbase';
1844
+
1845
+ // Get balance
1846
+ const balance = await credits.getBalance(workspaceId);
1847
+
1848
+ // Consume credits
1849
+ const result = await credits.consume(workspaceId, {
1850
+ quantity: 10,
1851
+ metadata: { action: 'generate-report' },
1852
+ });
1853
+
1854
+ // Public packages (no auth required)
1855
+ const publicPackages = await credits.getPublicPackages();
1856
+ ```
1857
+
1858
+ ## 🔖 Credit Gates
1859
+
1860
+ Control UI visibility based on credit balance. Credit data is loaded via `CreditBalanceContextProvider` (included in `SaaSOSProvider` by default).
1861
+
1862
+ ### Credit Gate Components
1863
+
1864
+ ```tsx
1865
+ import { WhenCreditsAvailable, WhenCreditsExhausted, WhenCreditsLow } from '@buildbase/sdk/react';
1866
+
1867
+ function CreditGatedUI() {
1868
+ return (
1869
+ <div>
1870
+ {/* Show when credits are available (default: min 1) */}
1871
+ <WhenCreditsAvailable>
1872
+ <GenerateButton />
1873
+ </WhenCreditsAvailable>
1874
+
1875
+ {/* Show when at least 10 credits are available */}
1876
+ <WhenCreditsAvailable min={10}>
1877
+ <BulkGenerateButton />
1878
+ </WhenCreditsAvailable>
1879
+
1880
+ {/* Show when credits are fully exhausted */}
1881
+ <WhenCreditsExhausted>
1882
+ <p>No credits remaining. Purchase more to continue.</p>
1883
+ </WhenCreditsExhausted>
1884
+
1885
+ {/* Show when credits fall below a threshold */}
1886
+ <WhenCreditsLow threshold={50}>
1887
+ <p>Running low on credits — only {balance} left.</p>
1888
+ </WhenCreditsLow>
1889
+ </div>
1890
+ );
1891
+ }
1892
+ ```
1893
+
1894
+ ### Credit Gates with Loading and Fallback
1895
+
1896
+ ```tsx
1897
+ <WhenCreditsAvailable
1898
+ loadingComponent={<Skeleton className="h-10" />}
1899
+ fallbackComponent={
1900
+ <p>
1901
+ No credits available. <a href="/buy">Buy credits</a>
1902
+ </p>
1903
+ }
1904
+ >
1905
+ <ActionButton />
1906
+ </WhenCreditsAvailable>
1907
+ ```
1908
+
1909
+ ### Credit Gate Components Reference
1910
+
1911
+ | Component | Renders children when | Props |
1912
+ | ---------------------- | -------------------------------------- | ----------------------------------------------------------------------------- |
1913
+ | `WhenCreditsAvailable` | `balance.available >= min` (default 1) | `min?`, `children`, `loadingComponent?`, `fallbackComponent?` |
1914
+ | `WhenCreditsExhausted` | `balance.available === 0` | `children`, `loadingComponent?`, `fallbackComponent?` |
1915
+ | `WhenCreditsLow` | `balance.available <= threshold` | `threshold` (required), `children`, `loadingComponent?`, `fallbackComponent?` |
1916
+
1917
+ All gates must be used inside `CreditBalanceContextProvider` (included in `SaaSOSProvider`). By default they return `null` while loading or when the condition is not met.
1918
+
1686
1919
  ## 📝 Beta Form Component
1687
1920
 
1688
1921
  Use the pre-built `BetaForm` component for signup/waitlist forms:
@@ -1786,12 +2019,15 @@ All SDK API clients extend a shared base class and are exported from the package
1786
2019
 
1787
2020
  ### Components
1788
2021
 
1789
- | Component | Purpose |
1790
- | ------------------------------------------------------------------- | ------------------------------------------------------------------- |
1791
- | `SubscriptionContextProvider` | Provides subscription data to children (included in SaaSOSProvider) |
1792
- | `WhenSubscription`, `WhenNoSubscription`, `WhenSubscriptionToPlans` | Subscription gate components |
1793
- | `WhenTrialing`, `WhenNotTrialing`, `WhenTrialEnding` | Trial gate components |
1794
- | `WhenQuotaAvailable`, `WhenQuotaExhausted`, `WhenQuotaOverage` | Quota gate components |
2022
+ | Component | Purpose |
2023
+ | ------------------------------------------------------------------- | --------------------------------------------------------------------- |
2024
+ | `SubscriptionContextProvider` | Provides subscription data to children (included in SaaSOSProvider) |
2025
+ | `WhenSubscription`, `WhenNoSubscription`, `WhenSubscriptionToPlans` | Subscription gate components |
2026
+ | `WhenTrialing`, `WhenNotTrialing`, `WhenTrialEnding` | Trial gate components |
2027
+ | `WhenQuotaAvailable`, `WhenQuotaExhausted`, `WhenQuotaOverage` | Quota gate components |
2028
+ | `CreditBalanceContextProvider` | Provides credit balance data to children (included in SaaSOSProvider) |
2029
+ | `WhenCreditsAvailable`, `WhenCreditsExhausted`, `WhenCreditsLow` | Credit gate components |
2030
+ | `CreditBalance`, `CreditActionsProvider` | Credit display and action components (render-prop pattern) |
1795
2031
 
1796
2032
  ### Currency, pricing variant & quota utilities
1797
2033
 
@@ -1820,21 +2056,23 @@ const workspaceApi = new WorkspaceApi({
1820
2056
 
1821
2057
  Prefer these SDK hooks for state and operations instead of `useAppSelector`:
1822
2058
 
1823
- | Hook | Purpose |
1824
- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1825
- | `useSaaSAuth()` | Auth state (user, session, status), signIn(returnUrl?), signOut, openWorkspaceSettings |
1826
- | `useSaaSWorkspaces()` | Workspaces, currentWorkspace, loading, switching/switchingToId, CRUD and switch actions |
1827
- | `useSaaSOs()` | OS config (serverUrl, version, orgId, auth, settings) when you need the full config object |
1828
- | `useSaaSSettings()` | Organization settings and getSettings (prefer this when you only need settings) |
1829
- | `useUserAttributes()` | User attributes and update/refresh |
1830
- | `useUserFeatures()` | User feature flags |
1831
- | `useSubscriptionContext()` | Subscription for current workspace (response, loading, refetch); use inside SubscriptionContextProvider |
1832
- | `useTrialStatus()` | Trial state: `isTrialing`, `daysRemaining`, `trialEndsAt`, `isTrialEnding` |
1833
- | `usePushNotifications()` | Push notification state and actions: `isSubscribed`, `subscribe()`, `unsubscribe()` |
1834
- | Subscription hooks | `usePublicPlans`, `useSubscription`, `useSubscriptionManagement`, `usePlanGroup`, `usePlanGroupVersions`, `usePublicPlanGroupVersion`, `useCreateCheckoutSession`, `useUpdateSubscription`, `useCancelSubscription`, `useResumeSubscription`, `useInvoices`, `useInvoice` |
1835
- | `useQuotaUsageContext()` | Quota usage for current workspace (quotas, loading, refetch); use inside QuotaUsageContextProvider |
1836
- | Quota usage hooks | `useRecordUsage`, `useQuotaUsageStatus`, `useAllQuotaUsage`, `useUsageLogs` |
1837
- | Invalidation helpers | `invalidateSubscription()`, `invalidateQuotaUsage()` — trigger context refetch after server-side mutations |
2059
+ | Hook | Purpose |
2060
+ | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2061
+ | `useSaaSAuth()` | Auth state (user, session, status), signIn(returnUrl?), signOut, openWorkspaceSettings |
2062
+ | `useSaaSWorkspaces()` | Workspaces, currentWorkspace, loading, switching/switchingToId, CRUD and switch actions |
2063
+ | `useSaaSOs()` | OS config (serverUrl, version, orgId, auth, settings) when you need the full config object |
2064
+ | `useSaaSSettings()` | Organization settings and getSettings (prefer this when you only need settings) |
2065
+ | `useUserAttributes()` | User attributes and update/refresh |
2066
+ | `useUserFeatures()` | User feature flags |
2067
+ | `useSubscriptionContext()` | Subscription for current workspace (response, loading, refetch); use inside SubscriptionContextProvider |
2068
+ | `useTrialStatus()` | Trial state: `isTrialing`, `daysRemaining`, `trialEndsAt`, `isTrialEnding` |
2069
+ | `usePushNotifications()` | Push notification state and actions: `isSubscribed`, `subscribe()`, `unsubscribe()` |
2070
+ | Subscription hooks | `usePublicPlans`, `useSubscription`, `useSubscriptionManagement`, `usePlanGroup`, `usePlanGroupVersions`, `usePublicPlanGroupVersion`, `useCreateCheckoutSession`, `useUpdateSubscription`, `useCancelSubscription`, `useResumeSubscription`, `useInvoices`, `useInvoice` |
2071
+ | `useQuotaUsageContext()` | Quota usage for current workspace (quotas, loading, refetch); use inside QuotaUsageContextProvider |
2072
+ | Quota usage hooks | `useRecordUsage`, `useQuotaUsageStatus`, `useAllQuotaUsage`, `useUsageLogs` |
2073
+ | `useCreditBalanceContext()` | Credit balance for current workspace (balance, loading, refetch); use inside CreditBalanceContextProvider |
2074
+ | Credit hooks | `useCreditBalance`, `useConsumeCredits`, `usePublicCreditPackages` |
2075
+ | Invalidation helpers | `invalidateSubscription()`, `invalidateQuotaUsage()`, `invalidateCreditBalance()` — trigger context refetch after server-side mutations |
1838
2076
 
1839
2077
  Using hooks keeps your code stable if internal state shape changes and avoids direct Redux/context coupling.
1840
2078
 
@@ -1856,16 +2094,16 @@ All TypeScript types are exported for type safety. See the [TypeScript definitio
1856
2094
 
1857
2095
  ### SaaSOSProvider Props
1858
2096
 
1859
- | Prop | Type | Required | Description |
1860
- | -------------------------- | --------------------------- | -------- | ------------------------------------------------------------------------------------------------ |
1861
- | `serverUrl` | `string` | ✅ | API server URL (must be valid URL) |
1862
- | `version` | `ApiVersion` | ✅ | API version (currently only `'v1'`) |
1863
- | `orgId` | `string` | ✅ | Organization ID (must be valid MongoDB ObjectId - 24 hex characters) |
1864
- | `auth` | `IAuthConfig` | ❌ | Authentication configuration |
1865
- | `locale` | `SDKLocale` | ❌ | SDK UI language (`'en'`, `'es'`, `'fr'`, `'de'`, `'ja'`, `'zh'`, `'hi'`, `'ar'`) |
1866
- | `defaultPermissions` | `Record<string, string[]>` | ❌ | Default app permissions per role |
1867
- | `getCheckoutStripeParams` | `GetCheckoutStripeParams` | ❌ | Async callback called before every Stripe checkout to return metadata, referral IDs, etc. |
1868
- | `children` | `ReactNode` | ✅ | React children |
2097
+ | Prop | Type | Required | Description |
2098
+ | ------------------------- | -------------------------- | -------- | ----------------------------------------------------------------------------------------- |
2099
+ | `serverUrl` | `string` | ✅ | API server URL (must be valid URL) |
2100
+ | `version` | `ApiVersion` | ✅ | API version (currently only `'v1'`) |
2101
+ | `orgId` | `string` | ✅ | Organization ID (must be valid MongoDB ObjectId - 24 hex characters) |
2102
+ | `auth` | `IAuthConfig` | ❌ | Authentication configuration |
2103
+ | `locale` | `SDKLocale` | ❌ | SDK UI language (`'en'`, `'es'`, `'fr'`, `'de'`, `'ja'`, `'zh'`, `'hi'`, `'ar'`) |
2104
+ | `defaultPermissions` | `Record<string, string[]>` | ❌ | Default app permissions per role |
2105
+ | `getCheckoutStripeParams` | `GetCheckoutStripeParams` | ❌ | Async callback called before every Stripe checkout to return metadata, referral IDs, etc. |
2106
+ | `children` | `ReactNode` | ✅ | React children |
1869
2107
 
1870
2108
  ### Auth Configuration
1871
2109
 
@@ -1880,6 +2118,8 @@ interface IAuthConfig {
1880
2118
  handleAuthentication: (code: string) => Promise<{ sessionId: string }>;
1881
2119
  // Required: clear session on sign out (clears httpOnly cookie on server)
1882
2120
  onSignOut: () => Promise<void>;
2121
+ // Optional: called when session is missing, expired, or invalid
2122
+ onSessionExpired?: (reason: 'missing' | 'expired' | 'invalid') => void;
1883
2123
  // Optional: listen to SDK events
1884
2124
  handleEvent?: (eventType: EventType, data: EventData) => void | Promise<void>;
1885
2125
  // Optional: called before workspace switch
@@ -2414,7 +2654,7 @@ await fetchWorkspaces();
2414
2654
 
2415
2655
  The SDK also works on the server — API routes, background jobs, webhooks, cron tasks. Zero React dependency.
2416
2656
 
2417
- ```
2657
+ ```text
2418
2658
  @buildbase/sdk Server: BuildBase() factory, API classes, types, utilities
2419
2659
  @buildbase/sdk/react Client: React hooks, providers, gate components (documented above)
2420
2660
  ```
@@ -2433,6 +2673,7 @@ export const {
2433
2673
  workspace,
2434
2674
  subscription,
2435
2675
  usage,
2676
+ credits,
2436
2677
  plans,
2437
2678
  invoices,
2438
2679
  users,
@@ -2522,18 +2763,19 @@ const sub = await bb.subscription.get(workspaceId);
2522
2763
 
2523
2764
  ### Server-Side Actions Reference
2524
2765
 
2525
- | Module | Methods |
2526
- | -------------- | ------------------------------------------------------------------------ |
2527
- | `workspace` | `list`, `get`, `create`, `update`, `delete` |
2528
- | `users` | `list`, `invite`, `remove`, `updateRole`, `getProfile`, `updateProfile` |
2529
- | `subscription` | `get`, `checkout`, `update`, `cancel`, `resume`, `getBillingPortalUrl` |
2530
- | `plans` | `getGroup`, `getVersions`, `getPublic`, `getVersion` |
2531
- | `invoices` | `list`, `get` |
2532
- | `usage` | `record`, `recordBatch`, `getQuota`, `getAll`, `getLogs` |
2533
- | `settings` | `get` |
2534
- | `features` | `list`, `update` |
2535
- | `notification` | `send(workspaceId, event, userId?, data?)` |
2536
- | `permissions` | `check(workspaceId, userId, permission)`, `resolve(workspaceId, userId)` |
2766
+ | Module | Methods |
2767
+ | -------------- | ----------------------------------------------------------------------------------------------------------------------- |
2768
+ | `workspace` | `list`, `get`, `create`, `update`, `delete` |
2769
+ | `users` | `list`, `invite`, `remove`, `updateRole`, `getProfile`, `updateProfile` |
2770
+ | `subscription` | `get`, `checkout`, `update`, `cancel`, `resume`, `getBillingPortalUrl` |
2771
+ | `plans` | `getGroup`, `getVersions`, `getPublic`, `getVersion` |
2772
+ | `invoices` | `list`, `get` |
2773
+ | `usage` | `record`, `recordBatch`, `getQuota`, `getAll`, `getLogs` |
2774
+ | `credits` | `getBalance`, `consume`, `purchase`, `getPackages`, `getTransactions`, `getExpiring`, `getBuckets`, `getPublicPackages` |
2775
+ | `settings` | `get` |
2776
+ | `features` | `list`, `update` |
2777
+ | `notification` | `send(workspaceId, event, userId?, data?)` |
2778
+ | `permissions` | `check(workspaceId, userId, permission)`, `resolve(workspaceId, userId)` |
2537
2779
 
2538
2780
  ### BuildBase Config Options
2539
2781
 
package/dist/index.d.ts CHANGED
@@ -95,6 +95,8 @@ type DunningStateType = (typeof DunningState)[keyof typeof DunningState];
95
95
  interface ISubscription {
96
96
  _id: string;
97
97
  subscriptionStatus: SubscriptionStatusType;
98
+ /** Stripe Subscription ID (e.g. 'sub_...'). Present when linked to Stripe. */
99
+ subscriptionId?: string;
98
100
  stripePriceId?: string;
99
101
  stripeCurrentPeriodEnd?: string;
100
102
  /** When the current monthly usage period ends (quotas reset on this date). */
@@ -533,12 +535,10 @@ interface IInvoice {
533
535
  subscription: string;
534
536
  }
535
537
  interface IInvoiceListResponse {
536
- success: boolean;
537
538
  invoices: IInvoice[];
538
539
  has_more: boolean;
539
540
  }
540
541
  interface IInvoiceResponse {
541
- success: boolean;
542
542
  invoice: IInvoice;
543
543
  }
544
544
  /** Request body for POST .../workspaces/:id/subscription/usage */
@@ -1057,6 +1057,8 @@ interface IBaseApiConfig {
1057
1057
  method: string;
1058
1058
  path: string;
1059
1059
  }) => void;
1060
+ /** Called when any request receives a 401 response (session expired mid-use). */
1061
+ onUnauthorized?: () => void;
1060
1062
  /** Custom fetch implementation. */
1061
1063
  fetch?: typeof globalThis.fetch;
1062
1064
  }
@@ -1080,6 +1082,7 @@ declare abstract class BaseApi {
1080
1082
  private readonly _debug;
1081
1083
  private readonly _customHeaders;
1082
1084
  private readonly _onError?;
1085
+ private readonly _onUnauthorized?;
1083
1086
  private readonly _fetch;
1084
1087
  constructor(config: IBaseApiConfig);
1085
1088
  /** Throws if config is not ready for API calls. Called automatically by fetchJson/fetchResponse. */
@@ -1108,6 +1111,16 @@ declare abstract class BaseApi {
1108
1111
  * Caller is responsible for checking response.ok and parsing body.
1109
1112
  */
1110
1113
  protected fetchResponse(path: string, init?: RequestInit): Promise<Response>;
1114
+ /**
1115
+ * Throw an error from a failed response.
1116
+ * Tries to parse JSON error body; falls back to status-based message.
1117
+ */
1118
+ protected throwResponseError(response: Response, defaultMsg: string): Promise<never>;
1119
+ /**
1120
+ * Parse JSON and unwrap `{ success, data }` envelope if present.
1121
+ * Throws if `success` is explicitly `false`.
1122
+ */
1123
+ protected unwrapResponse<T>(response: Response, defaultMsg: string): Promise<T>;
1111
1124
  }
1112
1125
 
1113
1126
  /**
@@ -1136,7 +1149,8 @@ declare class AuthApi extends BaseApi {
1136
1149
  });
1137
1150
  /**
1138
1151
  * Initiate OAuth sign-in flow. Returns redirect URL to the auth provider.
1139
- * Uses /api/v1/auth/request (no 'public' prefix — auth endpoints are at the root).
1152
+ * Uses /api/v1/auth/request — auth endpoints sit outside the /public basePath,
1153
+ * so we build the URL directly instead of using fetchResponse.
1140
1154
  */
1141
1155
  requestAuth(params: AuthRequestParams): Promise<AuthRequestResponse>;
1142
1156
  /** Fetch user profile with the given session ID (used after OAuth redirect or from storage). */
@@ -1316,6 +1330,7 @@ interface NotificationEvent {
1316
1330
  declare class WorkspaceApi extends BaseApi {
1317
1331
  constructor(config: IOsConfig & {
1318
1332
  sessionId?: string;
1333
+ onUnauthorized?: () => void;
1319
1334
  });
1320
1335
  getWorkspaces(): Promise<IWorkspace[]>;
1321
1336
  createWorkspace(data: {
@@ -1689,23 +1704,11 @@ declare class EventEmitter {
1689
1704
  }
1690
1705
  declare const eventEmitter: EventEmitter;
1691
1706
 
1692
- /**
1693
- * Notify all subscribers to refetch credit balance (e.g. after consume or purchase).
1694
- * Called internally by useConsumeCredits, usePurchaseCredits on success.
1695
- */
1696
- declare function invalidateCreditBalance(): void;
1707
+ declare const invalidateCreditBalance: () => void;
1697
1708
 
1698
- /**
1699
- * Notify all subscribers to refetch quota usage (e.g. after recording usage).
1700
- * Called internally by useRecordUsage on success.
1701
- */
1702
- declare function invalidateQuotaUsage(): void;
1709
+ declare const invalidateQuotaUsage: () => void;
1703
1710
 
1704
- /**
1705
- * Notify all subscribers to refetch subscription (e.g. after update/cancel/resume).
1706
- * Called internally by useUpdateSubscription, useCancelSubscription, useResumeSubscription on success.
1707
- */
1708
- declare function invalidateSubscription(): void;
1711
+ declare const invalidateSubscription: () => void;
1709
1712
 
1710
1713
  /**
1711
1714
  * BuildBase URL parameter utilities.
@@ -2166,7 +2169,7 @@ declare function hasPermission(permission: string | string[], ctx: PermissionCon
2166
2169
  *
2167
2170
  * Or manually create `public/push-sw.js` with this content.
2168
2171
  */
2169
- declare const 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 var payload = event.data.json();\n var title = payload.title || 'Notification';\n var 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 var 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 (var i = 0; i < clientList.length; i++) {\n var 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";
2172
+ declare const 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";
2170
2173
 
2171
2174
  interface BetaFormData {
2172
2175
  name?: string;