@buildbase/sdk 0.0.54 → 0.0.56
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 +8 -4
- package/CHANGELOG.md +55 -0
- package/LICENSE +21 -0
- package/README.md +294 -47
- package/dist/css/styles.css +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +367 -88
- package/dist/index.mjs +1 -1
- package/dist/mcp/index.cjs +1 -1
- package/dist/mcp/index.d.ts +332 -16
- package/dist/mcp/index.mjs +1 -1
- package/dist/react/chunks/SettingsDialog-CvoDs5eM.cjs +2 -0
- package/dist/react/chunks/SettingsDialog-VaOW2jtS.mjs +2 -0
- package/dist/react/chunks/SubscriptionDialog-8CagFA_V.cjs +2 -0
- package/dist/react/chunks/SubscriptionDialog-C5EgpXE0.mjs +2 -0
- package/dist/react/chunks/ar-CGbuXEeR.cjs +2 -0
- package/dist/react/chunks/ar-DnShbYvQ.mjs +2 -0
- package/dist/react/chunks/de-C3iuiLAz.mjs +2 -0
- package/dist/react/chunks/de-a1mX2rR4.cjs +2 -0
- package/dist/react/chunks/es-D4T7YE7x.cjs +2 -0
- package/dist/react/chunks/es-u9vKMeaX.mjs +2 -0
- package/dist/react/chunks/fr-Bnnz2yJZ.cjs +2 -0
- package/dist/react/chunks/fr-DjgpP202.mjs +2 -0
- package/dist/react/chunks/hi-B8Mjr8rU.cjs +2 -0
- package/dist/react/chunks/hi-Cu_9iCjb.mjs +2 -0
- package/dist/react/chunks/ja-B0eCVKdZ.cjs +2 -0
- package/dist/react/chunks/ja-DfJ5WUrG.mjs +2 -0
- package/dist/react/chunks/react-BBY-Vuzr.cjs +2 -0
- package/dist/react/chunks/react-CvU8uS25.mjs +2 -0
- package/dist/react/chunks/zh-BnPZ0j4w.cjs +2 -0
- package/dist/react/chunks/zh-JjoMmHmK.mjs +2 -0
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.d.ts +728 -142
- package/dist/react/index.mjs +1 -1
- package/docs/ARCHITECTURE.md +419 -0
- package/docs/ERROR_CODES.md +96 -0
- package/docs/MCP-AND-AGENT-READINESS.md +893 -0
- package/docs/THEMING.md +67 -0
- package/docs/UI-CONFIG.md +207 -0
- package/package.json +14 -3
- package/dist/react/chunks/SettingsDialog-B28WJsAr.cjs +0 -2
- package/dist/react/chunks/SettingsDialog-blhppbNm.mjs +0 -2
- package/dist/react/chunks/SubscriptionDialog-BTWVX3cH.mjs +0 -2
- package/dist/react/chunks/SubscriptionDialog-QKmwvJU1.cjs +0 -2
- package/dist/react/chunks/ar-1Z1MOGAP.mjs +0 -2
- package/dist/react/chunks/ar-VGzBv3AZ.cjs +0 -2
- package/dist/react/chunks/de-BjdDg60Y.cjs +0 -2
- package/dist/react/chunks/de-CXUItplO.mjs +0 -2
- package/dist/react/chunks/es-BBPsSZUp.cjs +0 -2
- package/dist/react/chunks/es-BTbzVutz.mjs +0 -2
- package/dist/react/chunks/fr-Cax48X_S.cjs +0 -2
- package/dist/react/chunks/fr-DPMeOQu3.mjs +0 -2
- package/dist/react/chunks/hi-BJqvpG6d.cjs +0 -2
- package/dist/react/chunks/hi-JfJx-LV7.mjs +0 -2
- package/dist/react/chunks/ja-CDGtLzTk.mjs +0 -2
- package/dist/react/chunks/ja-DvVyj5xX.cjs +0 -2
- package/dist/react/chunks/react-6QJCOE-D.cjs +0 -2
- package/dist/react/chunks/react-CC6T7Zky.mjs +0 -2
- package/dist/react/chunks/zh-BAcqxkxk.cjs +0 -2
- package/dist/react/chunks/zh-BNZ2Ooe8.mjs +0 -2
package/dist/react/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
export{
|
|
2
|
+
export{aP as ApiVersion,aQ as AuthApi,aR as AuthStatus,o as BBAction,aS as BBScreen,p as BBStatus,aT as BB_PARAM,aU as BaseApi,aV as BetaForm,ag as BillingIntervals,aW as BuildBaseProvider,aX as CURRENCY_DISPLAY,aY as CURRENCY_FLAG,aZ as ConnectMcpGuide,C as ConnectedAgents,a_ as ConnectedAgentsApi,a$ as CreditActionsProvider,b0 as CreditBalance,b1 as CreditBalanceContextProvider,b2 as CreditBucketSource,b3 as CreditBucketStatus,b4 as CreditStorePage,y as CreditTransactionType,b5 as DEFAULT_ROLE_PERMISSIONS,R as Devices,b6 as DevicesApi,av as DunningState,E as EmptyState,b7 as EventEmitter,b8 as FullScreenLoader,ae as InvoiceStatuses,L as LoadingState,b9 as PLAN_CURRENCY_CODES,ba as PLAN_CURRENCY_OPTIONS,bb as PUSH_SERVICE_WORKER_SCRIPT,P as Permission,bc as PricingPage,bd as PushApi,be as PushNotificationProvider,bf as QuotaUsageContextProvider,bg as SDKEvent,aM as SETTINGS_SCREENS,bh as SUPPORTED_LOCALES,aW as SaaSOSProvider,s as SectionHeader,T as Sessions,bi as SessionsApi,bj as SettingsApi,aF as SettingsScreen,aH as SidebarNavItem,aG as SidebarNavSection,r as StatusBanner,bk as SubscriptionContextProvider,ah as SubscriptionItemType,ak as SubscriptionStatus,bl as UserApi,bm as WhenAuthenticated,bn as WhenCreditsAvailable,bo as WhenCreditsExhausted,bp as WhenCreditsLow,bq as WhenNoSubscription,br as WhenNotTrialing,bs as WhenPermission,bt as WhenQuotaAvailable,bu as WhenQuotaExhausted,bv as WhenQuotaOverage,bw as WhenQuotaThreshold,bx as WhenRoles,by as WhenSubscription,bz as WhenSubscriptionToPlans,bA as WhenTrialEnding,bB as WhenTrialing,bC as WhenUnauthenticated,bD as WhenUserFeatureDisabled,bE as WhenUserFeatureEnabled,bF as WhenWorkspaceFeatureDisabled,bG as WhenWorkspaceFeatureEnabled,bH as WhenWorkspaceRoles,bI as WorkspaceApi,bJ as WorkspaceSwitcher,bK as calculateBillableSeats,bL as calculateSeatOverageCents,bM as calculateTotalSubscriptionCents,bN as cleanBBParams,bO as clearAuthIntent,bP as consumeAuthIntent,bQ as createBBUrl,ax as createCheckoutRedirectUrls,v as createCreditPurchaseRedirectUrls,bR as eventEmitter,bS as fillMcpTemplate,bT as formatCents,f as formatMinorAmountIntl,bU as formatOverageRate,bV as formatOverageRateWithLabel,bW as formatQuotaIncludedOverage,bX as formatQuotaWithPrice,aO as getAvailableCurrenciesFromPlans,as as getBasePriceCents,ar as getBillingIntervalAndCurrencyFromPriceId,e as getCurrencyDecimals,a0 as getCurrencyFlag,g as getCurrencySymbol,bY as getDisplayCurrency,au as getPerSeatPriceCents,aN as getPricingVariant,aj as getQuotaDisplayValue,ai as getQuotaDisplayWithVariant,aB as getQuotaOverageCents,bZ as getQuotaUnitLabelFromName,at as getSeatPricing,aw as getStripePriceIdForInterval,b_ as hasPermission,q as invalidateCreditBalance,b$ as invalidateQuotaUsage,ay as invalidateSubscription,c0 as isZeroDecimalCurrency,c1 as mcpServerKey,aJ as mergeUIConfig,c2 as minorAmountToDisplay,c3 as readBBParams,c4 as resolveMaxUsers,c5 as resolvePermissions,x as safeRedirect,c6 as saveAuthIntent,c7 as useAllQuotaUsage,ap as useBillingPortal,an as useCancelSubscription,c8 as useConnectedAgents,c9 as useConnectedAgentsApi,ca as useConsumeCredits,am as useCreateCheckoutSession,cb as useCreditBalance,j as useCreditBalanceContext,k as useCreditPackages,m as useCreditTransactions,cc as useDevices,cd as useDevicesApi,l as useExpiringCredits,ce as useFullScreenLoader,cf as useInvoice,ac as useInvoices,cg as useMcpConnection,h as usePermissions,ch as usePlanGroup,aq as usePlanGroupVersions,ci as usePublicCreditPackages,cj as usePublicPlanGroupVersion,ck as usePublicPlans,n as usePurchaseCredits,a7 as usePushNotifications,az as useQuotaUsageContext,cl as useQuotaUsageStatus,cm as useRecordUsage,ao as useResumeSubscription,aC as useSaaSAuth,cn as useSaaSOs,A as useSaaSSettings,z as useSaaSWorkspaces,aD as useSeatStatus,co as useSessions,cp as useSessionsApi,cq as useSubscription,aA as useSubscriptionContext,al as useSubscriptionManagement,u as useTranslation,cr as useTrialStatus,aI as useUIConfig,i as useUIVisibility,cs as useUpdateSubscription,ct as useUsageLogs,cu as useUserAttributes,cv as useUserFeatures,cw as validateInvite,cx as validateRedirectUrl,w as workspaceSettingsManager}from"./chunks/react-CvU8uS25.mjs";import"react/jsx-runtime";import"react";import"clsx";import"tailwind-merge";import"intl-messageformat";import"@hookform/resolvers/zod";import"react-hook-form";import"@radix-ui/react-slot";import"class-variance-authority";import"lucide-react";import"@radix-ui/react-label";import"zod";import"@radix-ui/react-avatar";import"@radix-ui/react-dialog";import"@radix-ui/react-icons";import"@radix-ui/react-radio-group";import"@radix-ui/react-scroll-area";import"@radix-ui/react-separator";import"@radix-ui/react-accordion";import"@radix-ui/react-alert-dialog";
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
# SDK Architecture Documentation
|
|
2
|
+
|
|
3
|
+
This document describes the internal architecture and design decisions of the BuildBase SDK.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The BuildBase SDK is a React-based SDK that provides authentication, workspace management, user management, and subscription features. It uses React Context for state management and follows a provider-based architecture.
|
|
8
|
+
|
|
9
|
+
## Architecture Layers
|
|
10
|
+
|
|
11
|
+
### 1. Provider Layer
|
|
12
|
+
|
|
13
|
+
The SDK uses a nested provider structure:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
SaaSOSProvider (root)
|
|
17
|
+
├── TranslationProvider (i18n)
|
|
18
|
+
├── SDKContextProvider (context management)
|
|
19
|
+
├── AuthProviderWrapper (authentication)
|
|
20
|
+
├── PortalProvider (portal rendering)
|
|
21
|
+
├── ContextConfigProvider (OS configuration)
|
|
22
|
+
├── CheckoutConfigProvider (Stripe checkout params)
|
|
23
|
+
├── PermissionConfigProvider (app permissions)
|
|
24
|
+
├── UserProvider (user attributes/features)
|
|
25
|
+
├── SubscriptionContextProvider (subscription for current workspace; powers subscription gates)
|
|
26
|
+
├── QuotaUsageContextProvider (quota usage; powers quota gates)
|
|
27
|
+
├── CreditBalanceContextProvider (credit balance; powers credit gates)
|
|
28
|
+
├── PushNotificationProvider (browser push)
|
|
29
|
+
└── WorkspaceSettingsProvider (workspace settings UI)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Design Decision**: Nested providers allow for:
|
|
33
|
+
|
|
34
|
+
- Clear separation of concerns
|
|
35
|
+
- Independent feature enablement
|
|
36
|
+
- Proper dependency ordering (auth before workspace, etc.)
|
|
37
|
+
|
|
38
|
+
### 2. Context Layer
|
|
39
|
+
|
|
40
|
+
The SDK uses multiple React contexts for state management:
|
|
41
|
+
|
|
42
|
+
#### AuthContext
|
|
43
|
+
|
|
44
|
+
- **Purpose**: Manages authentication state and session
|
|
45
|
+
- **State**: `{ session: AuthSession | null, status: AuthStatus }`
|
|
46
|
+
- **Design**: Single source of truth for auth state. Status enum drives all auth flags (derived, not stored).
|
|
47
|
+
|
|
48
|
+
#### OSContext
|
|
49
|
+
|
|
50
|
+
- **Purpose**: Manages SDK configuration (serverUrl, version, orgId, settings)
|
|
51
|
+
- **State**: `{ serverUrl, version, orgId, settings, auth }`
|
|
52
|
+
- **Design**: Immutable config that rarely changes after initialization.
|
|
53
|
+
|
|
54
|
+
#### WorkspaceContext
|
|
55
|
+
|
|
56
|
+
- **Purpose**: Manages workspace state and operations
|
|
57
|
+
- **State**: `{ workspaces[], currentWorkspace, loading, error, ... }`
|
|
58
|
+
- **Design**: Centralized workspace state with optimistic updates.
|
|
59
|
+
|
|
60
|
+
#### SDKContext
|
|
61
|
+
|
|
62
|
+
- **Purpose**: Combines all contexts into a single provider
|
|
63
|
+
- **Design**: Reduces provider nesting and provides unified state access.
|
|
64
|
+
|
|
65
|
+
#### SubscriptionContext
|
|
66
|
+
|
|
67
|
+
- **Purpose**: Provides subscription data for the current workspace to subscription gate components
|
|
68
|
+
- **State**: `{ response: ISubscriptionResponse | null, loading: boolean, refetch: () => Promise<void> }`
|
|
69
|
+
- **Design**: Read-only; data comes from `useSubscription(currentWorkspace?._id)`. Refetches when workspace changes or when subscription is invalidated (e.g. after plan update, cancel, resume). Internal invalidation notifier allows mutation hooks to trigger a refetch so gates stay in sync.
|
|
70
|
+
|
|
71
|
+
**Design Decision**: Multiple contexts instead of single context:
|
|
72
|
+
|
|
73
|
+
- Better performance (components only re-render when their context changes)
|
|
74
|
+
- Clearer separation of concerns
|
|
75
|
+
- Easier to test individual features
|
|
76
|
+
|
|
77
|
+
### 3. API Layer
|
|
78
|
+
|
|
79
|
+
The SDK uses a **central base class + domain APIs** pattern. All domain API classes extend `BaseApi` for shared URL building, auth headers, and request/response handling.
|
|
80
|
+
|
|
81
|
+
#### BaseApi (`src/lib/api-base.ts`)
|
|
82
|
+
|
|
83
|
+
- **Purpose**: Abstract base for all SDK API clients
|
|
84
|
+
- **Provides**: `baseUrl`, `getAuthHeaders()`, `url(path)`, `fetchJson<T>()`, `fetchResponse()`, `throwResponseError()`, `unwrapResponse<T>()`
|
|
85
|
+
- **Config**: `IBaseApiConfig` (serverUrl, version, optional orgId, onUnauthorized, timeout, maxRetries, debug, onError)
|
|
86
|
+
|
|
87
|
+
#### Domain APIs (extend BaseApi)
|
|
88
|
+
|
|
89
|
+
| Class | Location | Purpose |
|
|
90
|
+
| ---------------- | -------------------------------- | ----------------------------------------- |
|
|
91
|
+
| **UserApi** | `src/providers/user/api.ts` | User attributes, user features |
|
|
92
|
+
| **WorkspaceApi** | `src/providers/workspace/api.ts` | Workspaces, subscription, invoices, users |
|
|
93
|
+
| **SettingsApi** | `src/providers/os/api.ts` | Organization settings |
|
|
94
|
+
|
|
95
|
+
All are exported from the package; consumers typically use the high-level hooks (e.g. `useSaaSWorkspaces`, `useUserAttributes`) or instantiate API classes with config from `useSaaSOs()`.
|
|
96
|
+
|
|
97
|
+
#### API utilities (shared helpers)
|
|
98
|
+
|
|
99
|
+
- **Location**: `src/lib/api-utils.ts`
|
|
100
|
+
- **Functions**: `safeFetch`, `handleApiResponse`, `getErrorMessage`, `isAbortError`, `fetchWithTimeout`
|
|
101
|
+
- **Error helpers** (`src/lib/error-handler.ts`): `handleError`, `getHookErrorMessage`, `createSDKError`
|
|
102
|
+
|
|
103
|
+
#### Currency, pricing variant & quota utilities
|
|
104
|
+
|
|
105
|
+
Multi-currency plans use **pricing variants** (`IPricingVariant`) and **per-interval quotas** (`IQuotaByInterval`). These modules are in `src/api/` and are exported from the package:
|
|
106
|
+
|
|
107
|
+
| Module | Purpose |
|
|
108
|
+
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
109
|
+
| **currency-utils** | `CURRENCY_DISPLAY`, `CURRENCY_FLAG`, `PLAN_CURRENCY_CODES`, `PLAN_CURRENCY_OPTIONS`, `getCurrencySymbol`, `getCurrencyFlag`, `formatCents`, `formatOverageRate`, `formatOverageRateWithLabel`, `formatQuotaIncludedOverage`, `getQuotaUnitLabelFromName` |
|
|
110
|
+
| **pricing-variant-utils** | `getPricingVariant`, `getBasePriceCents`, `getStripePriceIdForInterval`, `getQuotaOverageCents`, `getQuotaDisplayWithVariant`, `getAvailableCurrenciesFromPlans`, `getDisplayCurrency`, `getBillingIntervalAndCurrencyFromPriceId`; types: `IPricingVariant`, `PlanVersionWithPricingVariants`, `QuotaDisplayWithOverage` |
|
|
111
|
+
| **quota-utils** | `getQuotaDisplayValue`, `formatQuotaWithPrice`; types: `QuotaDisplayValue`, `FormatQuotaWithPriceOptions` |
|
|
112
|
+
|
|
113
|
+
Plan and subscription types use `IPlanVersion.pricingVariants` (instead of single `basePricing`/`stripePrices`) and `IQuotaByInterval` / `IQuotaIntervalValue` for quotas and overages per billing interval.
|
|
114
|
+
|
|
115
|
+
**Design Decision**: Central base class + domain APIs:
|
|
116
|
+
|
|
117
|
+
- Single place for URL building, auth, and fetch behavior
|
|
118
|
+
- Easy to add new domains (extend BaseApi)
|
|
119
|
+
- Consistent error handling and logging
|
|
120
|
+
|
|
121
|
+
### 4. Hook Layer
|
|
122
|
+
|
|
123
|
+
The SDK exposes hooks for consuming context and performing operations. **Prefer these hooks over `useAppSelector`** for state access so the SDK can evolve internal state without breaking consumers.
|
|
124
|
+
|
|
125
|
+
#### Auth Hooks
|
|
126
|
+
|
|
127
|
+
- `useSaaSAuth()`: Main auth hook (user, session, status, signIn, signOut, openWorkspaceSettings)
|
|
128
|
+
|
|
129
|
+
#### OS Hooks
|
|
130
|
+
|
|
131
|
+
- `useSaaSOs()`: Full OS config (serverUrl, version, orgId, auth, settings). Use when you need the config object.
|
|
132
|
+
- `useSaaSSettings()`: Organization settings and getSettings. Prefer this when you only need settings.
|
|
133
|
+
|
|
134
|
+
#### Workspace Hooks
|
|
135
|
+
|
|
136
|
+
- `useSaaSWorkspaces()`: Main workspace management hook (workspaces, currentWorkspace, loading, switching, switchingToId, CRUD and switch actions)
|
|
137
|
+
- Subscription hooks: `useSubscription`, `usePlanGroup`, etc.
|
|
138
|
+
|
|
139
|
+
#### Subscription Context Hook
|
|
140
|
+
|
|
141
|
+
- `useSubscriptionContext()`: Returns subscription for current workspace (response, loading, refetch). Used by subscription gate components; available to app code when subscription data or manual refetch is needed. Must be used within SubscriptionContextProvider.
|
|
142
|
+
|
|
143
|
+
#### User Hooks
|
|
144
|
+
|
|
145
|
+
- `useUserAttributes()`: User attributes access
|
|
146
|
+
- `useUserFeatures()`: User feature flags
|
|
147
|
+
|
|
148
|
+
**Design Decision**: Hooks pattern:
|
|
149
|
+
|
|
150
|
+
- Familiar React API
|
|
151
|
+
- Automatic re-renders on state changes
|
|
152
|
+
- Easy to use in functional components
|
|
153
|
+
- Type-safe with TypeScript
|
|
154
|
+
- Single public API for state; internal use of `useAppSelector` is limited to hook/context implementation
|
|
155
|
+
|
|
156
|
+
### 5. Component Layer
|
|
157
|
+
|
|
158
|
+
The SDK provides conditional rendering components:
|
|
159
|
+
|
|
160
|
+
#### Auth Components
|
|
161
|
+
|
|
162
|
+
- `WhenAuthenticated`: Renders when user is authenticated
|
|
163
|
+
- `WhenUnauthenticated`: Renders when user is not authenticated
|
|
164
|
+
|
|
165
|
+
#### Role Components
|
|
166
|
+
|
|
167
|
+
- `WhenRoles`: Renders based on user's global role
|
|
168
|
+
- `WhenWorkspaceRoles`: Renders based on user's workspace role
|
|
169
|
+
|
|
170
|
+
#### Feature Components
|
|
171
|
+
|
|
172
|
+
- `WhenUserFeatureEnabled/Disabled`: Renders based on user feature flags
|
|
173
|
+
- `WhenWorkspaceFeatureEnabled/Disabled`: Renders based on workspace feature flags
|
|
174
|
+
|
|
175
|
+
#### Subscription Gate Components
|
|
176
|
+
|
|
177
|
+
- `WhenSubscription`: Renders when current workspace has an active subscription (any plan)
|
|
178
|
+
- `WhenNoSubscription`: Renders when current workspace has no subscription
|
|
179
|
+
- `WhenSubscriptionToPlans`: Renders when current workspace is subscribed to one of the given plan slugs
|
|
180
|
+
|
|
181
|
+
All subscription gates consume `useSubscriptionContext()` and must be used within `SubscriptionContextProvider` (included in SaaSOSProvider by default).
|
|
182
|
+
|
|
183
|
+
**Design Decision**: Conditional components:
|
|
184
|
+
|
|
185
|
+
- Declarative API for feature gating
|
|
186
|
+
- Reduces boilerplate in consumer apps
|
|
187
|
+
- Type-safe with TypeScript
|
|
188
|
+
|
|
189
|
+
## State Management
|
|
190
|
+
|
|
191
|
+
### Context-Based State (No Redux)
|
|
192
|
+
|
|
193
|
+
**Decision**: Use React Context instead of Redux.
|
|
194
|
+
|
|
195
|
+
**Rationale**:
|
|
196
|
+
|
|
197
|
+
- Simpler for SDK consumers (no Redux setup required)
|
|
198
|
+
- Less bundle size
|
|
199
|
+
- Sufficient for SDK's state management needs
|
|
200
|
+
- Better integration with React 19
|
|
201
|
+
|
|
202
|
+
### State Updates
|
|
203
|
+
|
|
204
|
+
State updates use reducer pattern:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
// Example: AuthContext reducer
|
|
208
|
+
function authReducer(state: IAuthState, action: AuthAction): IAuthState {
|
|
209
|
+
switch (action.type) {
|
|
210
|
+
case 'AUTHENTICATION_STARTED':
|
|
211
|
+
return { ...state, status: AuthStatus.redirecting };
|
|
212
|
+
// ...
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**Design Decision**: Reducer pattern:
|
|
218
|
+
|
|
219
|
+
- Predictable state updates
|
|
220
|
+
- Easy to debug (action-based)
|
|
221
|
+
- Type-safe with TypeScript
|
|
222
|
+
|
|
223
|
+
### Derived State
|
|
224
|
+
|
|
225
|
+
Some state is derived rather than stored:
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
// Auth flags are derived from status
|
|
229
|
+
const flags = getAuthFlags(auth.status);
|
|
230
|
+
// Returns: { isLoading, isAuthenticated, isRedirecting }
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Design Decision**: Derived state:
|
|
234
|
+
|
|
235
|
+
- Single source of truth (status enum)
|
|
236
|
+
- Prevents state inconsistencies
|
|
237
|
+
- Reduces state size
|
|
238
|
+
|
|
239
|
+
## Authentication Flow
|
|
240
|
+
|
|
241
|
+
### OAuth Flow
|
|
242
|
+
|
|
243
|
+
1. User calls `signIn()`
|
|
244
|
+
2. SDK redirects to OAuth provider
|
|
245
|
+
3. User authenticates with provider
|
|
246
|
+
4. Provider redirects back with `?code=...`
|
|
247
|
+
5. SDK exchanges code for session
|
|
248
|
+
6. Session stored in localStorage and context
|
|
249
|
+
|
|
250
|
+
### Session Management
|
|
251
|
+
|
|
252
|
+
- **Source of truth**: httpOnly cookie (set by your server, not readable by JS)
|
|
253
|
+
- **Hydration**: On mount, SDK calls `getSession()` callback to restore the session from your server endpoint
|
|
254
|
+
- **Validation**: Session validated by fetching user profile with the sessionId
|
|
255
|
+
- **Expiration**: 401 responses trigger `onSessionExpired` callback (during hydration and mid-session via `onUnauthorized` interceptor in BaseApi)
|
|
256
|
+
- **Guards**: Component-scoped refs prevent duplicate hydration across mount/remount cycles
|
|
257
|
+
|
|
258
|
+
**Design Decision**: httpOnly cookie + callback pattern (similar to next-auth):
|
|
259
|
+
|
|
260
|
+
- Secure: session token not accessible to JS (XSS-safe)
|
|
261
|
+
- Flexible: server controls cookie lifetime, httpOnly, secure flags
|
|
262
|
+
- Framework-agnostic: works with any server (Next.js, Express, Hono)
|
|
263
|
+
|
|
264
|
+
### Workspace Switching
|
|
265
|
+
|
|
266
|
+
- **Entry point**: `switchToWorkspace(id)` calls auth callback `onWorkspaceChange({ workspace, user, role })`, then updates current workspace and storage.
|
|
267
|
+
- **State**: `switchingToId` (ID being switched to) and derived `switching`; used for loaders and to avoid overwriting with stale results.
|
|
268
|
+
- **Concurrency**: A version ref ensures only the latest switch completes; rejections from `onWorkspaceChange` are caught so they do not surface as unhandled.
|
|
269
|
+
|
|
270
|
+
## Error Handling
|
|
271
|
+
|
|
272
|
+
### Error Handler
|
|
273
|
+
|
|
274
|
+
Centralized error handler (`src/lib/error-handler.ts`):
|
|
275
|
+
|
|
276
|
+
- Logs errors in development
|
|
277
|
+
- Supports custom error callbacks
|
|
278
|
+
- Provides error context (component, action, metadata)
|
|
279
|
+
- Creates standardized SDKError instances
|
|
280
|
+
|
|
281
|
+
### Error Boundaries
|
|
282
|
+
|
|
283
|
+
React Error Boundaries catch component errors:
|
|
284
|
+
|
|
285
|
+
- `SDKErrorBoundary`: Catches errors in SDK components
|
|
286
|
+
- Prevents app crashes
|
|
287
|
+
- Provides fallback UI
|
|
288
|
+
- Logs errors automatically
|
|
289
|
+
|
|
290
|
+
**Design Decision**: Multiple error handling layers:
|
|
291
|
+
|
|
292
|
+
- Network errors: Handled in API utilities
|
|
293
|
+
- Component errors: Handled by Error Boundaries
|
|
294
|
+
- Application errors: Handled by error handler
|
|
295
|
+
- User-facing errors: Can be customized via callbacks
|
|
296
|
+
|
|
297
|
+
## Performance Optimizations
|
|
298
|
+
|
|
299
|
+
### Memoization
|
|
300
|
+
|
|
301
|
+
- Context values memoized to prevent unnecessary re-renders
|
|
302
|
+
- Hook return values memoized
|
|
303
|
+
- API client instances memoized (recreated only when config changes)
|
|
304
|
+
|
|
305
|
+
### Request Deduplication
|
|
306
|
+
|
|
307
|
+
- Refs used to prevent duplicate concurrent requests
|
|
308
|
+
- Example: `fetchingProfileRef` in auth provider
|
|
309
|
+
|
|
310
|
+
### AbortController
|
|
311
|
+
|
|
312
|
+
- All API calls support AbortSignal
|
|
313
|
+
- Requests cancelled on unmount
|
|
314
|
+
- Prevents memory leaks and race conditions
|
|
315
|
+
|
|
316
|
+
**Design Decision**: AbortController pattern:
|
|
317
|
+
|
|
318
|
+
- Prevents race conditions
|
|
319
|
+
- Reduces unnecessary network traffic
|
|
320
|
+
- Better user experience (no stale data)
|
|
321
|
+
|
|
322
|
+
## Type Safety
|
|
323
|
+
|
|
324
|
+
### TypeScript Usage
|
|
325
|
+
|
|
326
|
+
- Full TypeScript coverage
|
|
327
|
+
- Strict type checking
|
|
328
|
+
- Exported types for all public APIs
|
|
329
|
+
|
|
330
|
+
### Type Exports
|
|
331
|
+
|
|
332
|
+
All public types exported from `src/index.ts`:
|
|
333
|
+
|
|
334
|
+
- API types (subscriptions, invoices, plans, pricing variants, quotas, etc.)
|
|
335
|
+
- Plan/subscription: `IPlanVersion`, `IPricingVariant`, `IQuotaByInterval`, `IQuotaIntervalValue`, `IStripePricesByInterval`, `IQuotaOveragesByInterval`, `IPublicPlanVersion`, `IPublicPlansResponse`, and related
|
|
336
|
+
- Currency/quota helpers: `QuotaDisplayValue`, `FormatQuotaWithPriceOptions`, `PlanVersionWithPricingVariants`, `QuotaDisplayWithOverage`
|
|
337
|
+
- Context types
|
|
338
|
+
- Error types
|
|
339
|
+
- Component prop types
|
|
340
|
+
|
|
341
|
+
**Design Decision**: Comprehensive type exports:
|
|
342
|
+
|
|
343
|
+
- Better developer experience
|
|
344
|
+
- Type-safe SDK usage
|
|
345
|
+
- IDE autocomplete support
|
|
346
|
+
|
|
347
|
+
## Event System
|
|
348
|
+
|
|
349
|
+
### Event Emitter
|
|
350
|
+
|
|
351
|
+
The SDK includes an event emitter for workspace/user events:
|
|
352
|
+
|
|
353
|
+
- `workspace:created`
|
|
354
|
+
- `workspace:updated`
|
|
355
|
+
- `workspace:deleted`
|
|
356
|
+
- `workspace:changed`
|
|
357
|
+
- `user:created`
|
|
358
|
+
- `user:updated`
|
|
359
|
+
- etc.
|
|
360
|
+
|
|
361
|
+
**Design Decision**: Event system:
|
|
362
|
+
|
|
363
|
+
- Allows consumers to react to SDK events
|
|
364
|
+
- Decouples SDK from consumer app logic
|
|
365
|
+
- Supports custom event handlers via callbacks
|
|
366
|
+
|
|
367
|
+
## Testing Considerations
|
|
368
|
+
|
|
369
|
+
### Testability
|
|
370
|
+
|
|
371
|
+
- Hooks can be tested with `@testing-library/react-hooks`
|
|
372
|
+
- Contexts can be tested independently
|
|
373
|
+
- API clients can be mocked
|
|
374
|
+
- Components can be tested with React Testing Library
|
|
375
|
+
|
|
376
|
+
### Mocking
|
|
377
|
+
|
|
378
|
+
- API calls can be mocked at the fetch level
|
|
379
|
+
- Contexts can be mocked with custom providers
|
|
380
|
+
- Event emitter can be mocked
|
|
381
|
+
|
|
382
|
+
## Bundle Size
|
|
383
|
+
|
|
384
|
+
### Tree Shaking
|
|
385
|
+
|
|
386
|
+
- Named exports for all public APIs
|
|
387
|
+
- No default exports (except ErrorBoundary)
|
|
388
|
+
- Side-effect-free modules where possible
|
|
389
|
+
|
|
390
|
+
### Dependencies
|
|
391
|
+
|
|
392
|
+
- Minimal external dependencies
|
|
393
|
+
- Peer dependencies for React (not bundled)
|
|
394
|
+
- Radix UI components (tree-shakeable)
|
|
395
|
+
|
|
396
|
+
## Future Considerations
|
|
397
|
+
|
|
398
|
+
- **Request Caching**: Optional cache for GET requests
|
|
399
|
+
- **Request Interceptors**: Optional request/response interceptors
|
|
400
|
+
- **Offline Support**: Optional response caching
|
|
401
|
+
- **Migration**: Backward compatible API; deprecation warnings before breaking changes; semantic versioning
|
|
402
|
+
|
|
403
|
+
## Summary
|
|
404
|
+
|
|
405
|
+
The BuildBase SDK follows a layered architecture:
|
|
406
|
+
|
|
407
|
+
1. **Providers**: Nested React providers for feature enablement
|
|
408
|
+
2. **Contexts**: Multiple React contexts for state management
|
|
409
|
+
3. **APIs**: Centralized API clients with shared utilities
|
|
410
|
+
4. **Hooks**: React hooks for consuming SDK features
|
|
411
|
+
5. **Components**: Conditional rendering components
|
|
412
|
+
|
|
413
|
+
Key design principles:
|
|
414
|
+
|
|
415
|
+
- **Simplicity**: Easy to use, minimal setup
|
|
416
|
+
- **Type Safety**: Full TypeScript coverage
|
|
417
|
+
- **Performance**: Memoization, request deduplication, AbortController
|
|
418
|
+
- **Error Handling**: Multiple layers of error handling
|
|
419
|
+
- **Developer Experience**: Comprehensive documentation, JSDoc comments, examples
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Error Codes Reference
|
|
2
|
+
|
|
3
|
+
This document lists error codes used in the BuildBase SDK and their meanings. These codes can be used with `createSDKError()` and may be set on `SDKError.code` when the SDK throws. Not every code is used in every release; this doc serves as the canonical reference.
|
|
4
|
+
|
|
5
|
+
## SDK Error Codes
|
|
6
|
+
|
|
7
|
+
### Authentication Errors
|
|
8
|
+
|
|
9
|
+
| Code | Description | When It Occurs |
|
|
10
|
+
| ----------------- | ----------------------- | ----------------------------------------------------------------------- |
|
|
11
|
+
| `AUTH_FAILED` | Authentication failed | When OAuth flow fails or user cannot be authenticated |
|
|
12
|
+
| `AUTH_REQUIRED` | Authentication required | When an operation requires authentication but user is not authenticated |
|
|
13
|
+
| `SESSION_EXPIRED` | Session expired | When user session has expired and needs to be refreshed |
|
|
14
|
+
| `INVALID_SESSION` | Invalid session | When session data is corrupted or invalid |
|
|
15
|
+
|
|
16
|
+
### Network Errors
|
|
17
|
+
|
|
18
|
+
| Code | Description | When It Occurs |
|
|
19
|
+
| ----------------- | ------------------------- | ----------------------------------------------------------- |
|
|
20
|
+
| `NETWORK_ERROR` | Network connection failed | When fetch fails due to network issues (offline, DNS, CORS) |
|
|
21
|
+
| `REQUEST_TIMEOUT` | Request timeout | When request exceeds timeout duration |
|
|
22
|
+
| `ABORT_ERROR` | Request aborted | When request is cancelled via AbortController |
|
|
23
|
+
|
|
24
|
+
### API Errors
|
|
25
|
+
|
|
26
|
+
| Code | Description | When It Occurs |
|
|
27
|
+
| -------------- | ------------------- | ------------------------------------------ |
|
|
28
|
+
| `API_ERROR` | Generic API error | When API returns an error response |
|
|
29
|
+
| `BAD_REQUEST` | Bad request (400) | When request is malformed or invalid |
|
|
30
|
+
| `UNAUTHORIZED` | Unauthorized (401) | When authentication is required or invalid |
|
|
31
|
+
| `FORBIDDEN` | Forbidden (403) | When user doesn't have permission |
|
|
32
|
+
| `NOT_FOUND` | Not found (404) | When resource doesn't exist |
|
|
33
|
+
| `SERVER_ERROR` | Server error (500+) | When server encounters an error |
|
|
34
|
+
|
|
35
|
+
### Workspace Errors
|
|
36
|
+
|
|
37
|
+
| Code | Description | When It Occurs |
|
|
38
|
+
| -------------------------- | ------------------------ | -------------------------------------------------------- |
|
|
39
|
+
| `WORKSPACE_NOT_FOUND` | Workspace not found | When workspace doesn't exist or user doesn't have access |
|
|
40
|
+
| `WORKSPACE_ACCESS_DENIED` | Workspace access denied | When user doesn't have permission to access workspace |
|
|
41
|
+
| `WORKSPACE_OWNER_REQUIRED` | Workspace owner required | When operation requires workspace owner (e.g., delete) |
|
|
42
|
+
| `INVALID_WORKSPACE_ID` | Invalid workspace ID | When workspace ID format is invalid |
|
|
43
|
+
|
|
44
|
+
### User Management Errors
|
|
45
|
+
|
|
46
|
+
| Code | Description | When It Occurs |
|
|
47
|
+
| -------------------------- | ------------------------ | -------------------------------------------- |
|
|
48
|
+
| `USER_NOT_FOUND` | User not found | When user doesn't exist |
|
|
49
|
+
| `USER_ALREADY_EXISTS` | User already exists | When trying to add user that already exists |
|
|
50
|
+
| `CANNOT_REMOVE_OWNER` | Cannot remove owner | When trying to remove workspace owner |
|
|
51
|
+
| `CANNOT_CHANGE_OWNER_ROLE` | Cannot change owner role | When trying to change workspace owner's role |
|
|
52
|
+
|
|
53
|
+
### Subscription Errors
|
|
54
|
+
|
|
55
|
+
| Code | Description | When It Occurs |
|
|
56
|
+
| ------------------------ | ---------------------- | ------------------------------------ |
|
|
57
|
+
| `SUBSCRIPTION_NOT_FOUND` | Subscription not found | When workspace has no subscription |
|
|
58
|
+
| `SUBSCRIPTION_ERROR` | Subscription error | When subscription operation fails |
|
|
59
|
+
| `CHECKOUT_ERROR` | Checkout error | When checkout session creation fails |
|
|
60
|
+
| `PLAN_NOT_FOUND` | Plan not found | When plan doesn't exist |
|
|
61
|
+
|
|
62
|
+
### Configuration Errors
|
|
63
|
+
|
|
64
|
+
| Code | Description | When It Occurs |
|
|
65
|
+
| -------------------- | ----------------------- | ------------------------------------------ |
|
|
66
|
+
| `INVALID_CONFIG` | Invalid configuration | When SDK configuration is invalid |
|
|
67
|
+
| `INVALID_SERVER_URL` | Invalid server URL | When serverUrl is not a valid URL |
|
|
68
|
+
| `INVALID_ORG_ID` | Invalid organization ID | When orgId is not a valid MongoDB ObjectId |
|
|
69
|
+
| `INVALID_VERSION` | Invalid API version | When API version is not supported |
|
|
70
|
+
|
|
71
|
+
### Generic Errors
|
|
72
|
+
|
|
73
|
+
| Code | Description | When It Occurs |
|
|
74
|
+
| ------------------ | ---------------- | ------------------------------------ |
|
|
75
|
+
| `UNKNOWN_ERROR` | Unknown error | When error type cannot be determined |
|
|
76
|
+
| `VALIDATION_ERROR` | Validation error | When input validation fails |
|
|
77
|
+
|
|
78
|
+
## HTTP Status Code Mappings
|
|
79
|
+
|
|
80
|
+
The SDK automatically maps HTTP status codes to error messages:
|
|
81
|
+
|
|
82
|
+
| Status Code | Error Message |
|
|
83
|
+
| ----------- | ---------------------------------------- |
|
|
84
|
+
| 400 | Bad Request |
|
|
85
|
+
| 401 | Unauthorized - Please check your session |
|
|
86
|
+
| 403 | Forbidden - You do not have permission |
|
|
87
|
+
| 404 | Resource not found |
|
|
88
|
+
| 500 | Internal server error |
|
|
89
|
+
| 502 | Bad Gateway |
|
|
90
|
+
| 503 | Service unavailable |
|
|
91
|
+
|
|
92
|
+
## Error Handling
|
|
93
|
+
|
|
94
|
+
- **Hook errors**: Use the `error` property from hooks (e.g. `useSaaSWorkspaces()`, `useUserAttributes()`) and show user feedback (toast, inline message).
|
|
95
|
+
- **Async operations**: In `catch` blocks, check `error.message`; for aborted requests, ignore or treat as cancelled.
|
|
96
|
+
- **React errors**: Wrap your app with an error boundary to catch render-time errors.
|