@buildbase/sdk 0.0.27 → 0.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,4 @@
1
- import * as react from 'react';
2
- import react__default, { ReactNode } from 'react';
3
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import { z } from 'zod';
4
2
 
5
3
  interface IDocument {
6
4
  _id: string;
@@ -27,6 +25,32 @@ interface IUser extends IDocument {
27
25
  currency?: string;
28
26
  attributes?: Record<string, string | number | boolean>;
29
27
  }
28
+ interface IAsset extends IDocument {
29
+ _id: string;
30
+ createdAt: string;
31
+ updatedAt: string;
32
+ deleted: boolean;
33
+ name: string;
34
+ uniqueName: string;
35
+ mimeType: string;
36
+ size: number;
37
+ encoding: string;
38
+ bucket: {
39
+ name: string;
40
+ url: string;
41
+ path: string;
42
+ };
43
+ metadata: {
44
+ [key: string]: string | number | boolean | object | null;
45
+ };
46
+ image?: {
47
+ width: number;
48
+ height: number;
49
+ };
50
+ tags: string[];
51
+ public: boolean;
52
+ creator: string | IUser;
53
+ }
30
54
  type BillingInterval = 'monthly' | 'yearly' | 'quarterly';
31
55
  interface ISubscription {
32
56
  _id: string;
@@ -595,11 +619,6 @@ interface AuthUser {
595
619
  role: string;
596
620
  image?: string;
597
621
  }
598
- interface AuthSession {
599
- user: AuthUser;
600
- sessionId: string;
601
- expires: string;
602
- }
603
622
  interface IAuthConfig {
604
623
  clientId: string;
605
624
  redirectUrl: string;
@@ -610,6 +629,23 @@ interface IAuthCallbacks {
610
629
  sessionId: string;
611
630
  }>;
612
631
  onSignOut: () => Promise<void>;
632
+ /**
633
+ * Called on page refresh to restore the session.
634
+ * Return the sessionId if the user is still authenticated, or null to log out.
635
+ *
636
+ * The SDK calls this instead of reading from localStorage.
637
+ * Implement this to read from an httpOnly cookie via a server endpoint.
638
+ *
639
+ * @example Next.js — read from httpOnly cookie via API route
640
+ * ```ts
641
+ * getSession: async () => {
642
+ * const res = await fetch('/api/auth/session')
643
+ * const data = await res.json()
644
+ * return data.sessionId ?? null
645
+ * }
646
+ * ```
647
+ */
648
+ getSession: () => Promise<string | null>;
613
649
  /**
614
650
  * Event handler for User and Workspace events
615
651
  * @param eventType - The type of event that occurred
@@ -655,975 +691,232 @@ interface IOsState extends IOsConfig {
655
691
  settings?: ISettings | null;
656
692
  }
657
693
 
658
- interface SaaSOSProviderProps extends IOsState {
659
- children: react__default.ReactNode;
660
- }
661
- declare const SaaSOSProvider: react__default.FC<SaaSOSProviderProps>;
662
-
663
- type Language = 'en' | 'es' | 'fr' | 'de' | 'zh' | 'ja' | 'ko';
664
- interface FormText {
665
- nameLabel: string;
666
- emailLabel: string;
667
- submitText: string;
668
- submittingText: string;
669
- errorMessage: string;
670
- }
671
- interface BetaFormProps {
672
- onSuccess?: () => void;
673
- onError?: (error: string) => void;
674
- className?: string;
675
- fieldClassName?: string;
676
- language?: Language;
677
- customTexts?: Partial<FormText>;
678
- autoFocus?: boolean;
679
- showSuccessMessage?: boolean;
680
- successMessageDuration?: number;
681
- hideLogo?: boolean;
682
- hideTitles?: boolean;
683
- }
684
- declare const BetaForm: react__default.FC<BetaFormProps>;
685
-
686
- interface PricingPageDetails {
687
- /** Whether plan data is being fetched */
688
- loading: boolean;
689
- /** Error message if fetch failed */
690
- error: string | null;
691
- /** Subscription items (features, limits, quotas) */
692
- items: IPublicPlanItem[];
693
- /** Plan versions with pricing, features, limits, quotas */
694
- plans: IPublicPlanVersion[];
695
- /** Optional note from API (e.g. "Pricing is in cents. Please convert to dollars for display.") */
696
- notes?: string;
697
- /** Refetch plan data */
698
- refetch: () => Promise<void>;
699
- }
700
- interface PricingPageProps {
701
- /** Plan group slug (e.g. 'main-pricing', 'enterprise') */
702
- slug: string;
703
- /** Render prop receiving plan details - construct layout from items and plans */
704
- children: (details: PricingPageDetails) => ReactNode;
705
- /** Custom loading UI. Defaults to skeleton. */
706
- loadingFallback?: ReactNode;
707
- /** Custom error UI. Receives error message. */
708
- errorFallback?: (error: string) => ReactNode;
709
- }
710
694
  /**
711
- * Fetches and provides plan/pricing details for public pricing pages (no login required).
712
- * Returns items (features, limits, quotas) and plans (with pricing) - user constructs layout.
695
+ * Base API client for SDK endpoints.
696
+ * All domain API classes (WorkspaceApi, UserApi, etc.) extend this to share
697
+ * URL building, auth headers, and request/response handling.
713
698
  *
714
- * @example
715
- * ```tsx
716
- * <PricingPage slug="main-pricing">
717
- * {({ loading, error, items, plans, refetch }) => {
718
- * if (loading) return <Loading />;
719
- * if (error) return <Error message={error} />;
699
+ * Supports two auth modes:
700
+ * 1. **Client-side (default):** reads sessionId from localStorage automatically.
701
+ * 2. **Server-side:** pass `sessionId` in config — no localStorage access.
720
702
  *
721
- * return (
722
- * <div>
723
- * {plans.map(plan => (
724
- * <PlanCard key={plan._id} plan={plan} items={items} />
725
- * ))}
726
- * </div>
727
- * );
728
- * }}
729
- * </PricingPage>
703
+ * @example Server-side usage
704
+ * ```ts
705
+ * const api = new WorkspaceApi({
706
+ * serverUrl: "https://api.buildbase.app",
707
+ * version: "v1",
708
+ * orgId: "...",
709
+ * sessionId: req.headers["x-session-id"], // from your request
710
+ * });
711
+ * const sub = await api.getCurrentSubscription(workspaceId);
730
712
  * ```
731
713
  */
732
- declare function PricingPage({ slug, children, loadingFallback, errorFallback }: PricingPageProps): react_jsx_runtime.JSX.Element;
733
714
 
734
- interface IProps$2 {
735
- children: React.ReactNode;
715
+ interface IBaseApiConfig {
716
+ serverUrl: string;
717
+ version: ApiVersion;
718
+ orgId?: string;
719
+ /** When true, ensureReady() also requires orgId. Used by WorkspaceApi and SettingsApi. */
720
+ requireOrgId?: boolean;
721
+ /** API path segment after version (default 'public'). e.g. 'public' => .../v1/public, 'beta' => .../v1/beta */
722
+ basePath?: string;
723
+ /**
724
+ * Session ID for server-side usage. When provided, this is used for auth
725
+ * instead of reading from localStorage. Pass the session token from your
726
+ * request headers, cookies, or any server-side session store.
727
+ */
728
+ sessionId?: string;
729
+ /** Request timeout in milliseconds. 0 = no timeout. */
730
+ timeout?: number;
731
+ /** Max automatic retries for network errors / 5xx. */
732
+ maxRetries?: number;
733
+ /** Enable debug logging for all requests. */
734
+ debug?: boolean;
735
+ /** Custom headers merged into every request. */
736
+ headers?: Record<string, string>;
737
+ /** Error callback — called before error is thrown. */
738
+ onError?: (error: Error, context: {
739
+ method: string;
740
+ path: string;
741
+ }) => void;
742
+ /** Custom fetch implementation. */
743
+ fetch?: typeof globalThis.fetch;
736
744
  }
737
745
  /**
738
- * Conditional component that renders children only when user is authenticated.
739
- * Returns null if user is not authenticated or authentication status is still loading.
740
- *
741
- * @param props - Component props
742
- * @param props.children - Content to render when authenticated
743
- *
744
- * @example
745
- * ```tsx
746
- * function App() {
747
- * return (
748
- * <SaaSOSProvider {...config}>
749
- * <WhenAuthenticated>
750
- * <Dashboard />
751
- * </WhenAuthenticated>
752
- * <WhenUnauthenticated>
753
- * <LoginPage />
754
- * </WhenUnauthenticated>
755
- * </SaaSOSProvider>
756
- * );
757
- * }
758
- * ```
759
- */
760
- declare const WhenAuthenticated: {
761
- (props: IProps$2): react.ReactNode;
762
- displayName: string;
763
- };
764
- /**
765
- * Conditional component that renders children only when user is NOT authenticated.
766
- * Returns null if user is authenticated.
767
- * Note: Also renders during loading/redirecting states (when not yet authenticated).
768
- *
769
- * @param props - Component props
770
- * @param props.children - Content to render when unauthenticated
771
- *
772
- * @example
773
- * ```tsx
774
- * function App() {
775
- * return (
776
- * <SaaSOSProvider {...config}>
777
- * <WhenUnauthenticated>
778
- * <LoginPage />
779
- * </WhenUnauthenticated>
780
- * <WhenAuthenticated>
781
- * <Dashboard />
782
- * </WhenAuthenticated>
783
- * </SaaSOSProvider>
784
- * );
785
- * }
786
- * ```
787
- *
788
- * @example
789
- * ```tsx
790
- * // Handle loading state separately
791
- * function App() {
792
- * const { isLoading } = useSaaSAuth();
793
- *
794
- * if (isLoading) return <LoadingSpinner />;
795
- *
796
- * return (
797
- * <>
798
- * <WhenUnauthenticated><LoginPage /></WhenUnauthenticated>
799
- * <WhenAuthenticated><Dashboard /></WhenAuthenticated>
800
- * </>
801
- * );
802
- * }
803
- * ```
746
+ * Base class for SDK API clients.
747
+ * Provides:
748
+ * - baseUrl: `${serverUrl}/api/${version}/public`
749
+ * - getAuthHeaders()
750
+ * - fetchJson<T>(path, init, errorMessage): GET/POST/etc. with handleApiResponse
751
+ * - fetchResponse(path, init): raw Response for custom parsing (e.g. non-JSON or custom error handling)
804
752
  */
805
- declare const WhenUnauthenticated: {
806
- (props: IProps$2): react.ReactNode;
807
- displayName: string;
808
- };
809
-
810
- interface IProps$1 {
811
- roles: string[];
812
- children: React.ReactNode;
813
- fallback?: React.ReactNode;
753
+ declare abstract class BaseApi {
754
+ protected readonly serverUrl: string;
755
+ protected readonly version: ApiVersion;
756
+ protected readonly orgId: string | undefined;
757
+ private readonly requireOrgId;
758
+ private readonly basePath;
759
+ private readonly _sessionId;
760
+ private readonly _timeout;
761
+ private readonly _maxRetries;
762
+ private readonly _debug;
763
+ private readonly _customHeaders;
764
+ private readonly _onError?;
765
+ private readonly _fetch;
766
+ constructor(config: IBaseApiConfig);
767
+ /** Throws if config is not ready for API calls. Called automatically by fetchJson/fetchResponse. */
768
+ protected ensureReady(): void;
769
+ /** Base URL: ${serverUrl}/api/${version}/${basePath} */
770
+ protected get baseUrl(): string;
771
+ /**
772
+ * Auth headers (x-session-id).
773
+ * Uses injected sessionId (server-side) or falls back to localStorage (client-side).
774
+ * Subclasses can override to add more headers.
775
+ */
776
+ protected getAuthHeaders(): Record<string, string>;
777
+ /** Build full URL from path (path can be "workspaces" or "/workspaces"). */
778
+ protected url(path: string): string;
779
+ /** Merge auth + custom + per-request headers. */
780
+ private buildHeaders;
781
+ /** Execute fetch with timeout, retries, debug logging, and error callback. */
782
+ private executeFetch;
783
+ /**
784
+ * Execute request and parse JSON with handleApiResponse (throws on !response.ok).
785
+ * Use for standard JSON APIs.
786
+ */
787
+ protected fetchJson<T>(path: string, init?: RequestInit, errorMessage?: string): Promise<T>;
788
+ /**
789
+ * Execute request and return raw Response (for custom parsing or error handling).
790
+ * Caller is responsible for checking response.ok and parsing body.
791
+ */
792
+ protected fetchResponse(path: string, init?: RequestInit): Promise<Response>;
814
793
  }
794
+
815
795
  /**
816
- * Conditional component that renders children only when user has one of the specified roles.
817
- * Checks the user's global role (not workspace-specific).
818
- *
819
- * @param props - Component props
820
- * @param props.roles - Array of role strings to check against user's role
821
- * @param props.children - Content to render when user has matching role
822
- * @param props.fallback - Optional content to render when user doesn't have matching role (default: null)
823
- *
824
- * @example
825
- * ```tsx
826
- * function AdminPanel() {
827
- * return (
828
- * <WhenRoles roles={['admin', 'super-admin']}>
829
- * <AdminContent />
830
- * </WhenRoles>
831
- * );
832
- * }
833
- * ```
834
- *
835
- * @example
836
- * ```tsx
837
- * // With fallback
838
- * function SettingsPage() {
839
- * return (
840
- * <WhenRoles
841
- * roles={['admin']}
842
- * fallback={<p>You don't have permission to access this page.</p>}
843
- * >
844
- * <AdminSettings />
845
- * </WhenRoles>
846
- * );
847
- * }
848
- * ```
849
- */
850
- declare const WhenRoles: {
851
- (props: IProps$1): react.ReactNode;
852
- displayName: string;
853
- };
854
- /**
855
- * Conditional component that renders children only when user has one of the specified roles
856
- * in the current workspace. Checks workspace-specific role, not global role.
857
- *
858
- * @param props - Component props
859
- * @param props.roles - Array of role strings to check against user's workspace role
860
- * @param props.children - Content to render when user has matching workspace role
861
- * @param props.fallback - Optional content to render when user doesn't have matching role (default: null)
862
- *
863
- * @example
864
- * ```tsx
865
- * function WorkspaceSettings() {
866
- * return (
867
- * <WhenWorkspaceRoles roles={['owner', 'admin']}>
868
- * <SettingsContent />
869
- * </WhenWorkspaceRoles>
870
- * );
871
- * }
872
- * ```
873
- *
874
- * @example
875
- * ```tsx
876
- * // Edge case: User not in current workspace
877
- * function WorkspaceContent() {
878
- * return (
879
- * <WhenWorkspaceRoles
880
- * roles={['member']}
881
- * fallback={<p>You are not a member of this workspace.</p>}
882
- * >
883
- * <WorkspaceDashboard />
884
- * </WhenWorkspaceRoles>
885
- * );
886
- * }
887
- * ```
796
+ * API client for authentication.
797
+ * Extends BaseApi for shared URL/auth/request handling.
888
798
  */
889
- declare const WhenWorkspaceRoles: {
890
- (props: IProps$1): react.ReactNode;
891
- displayName: string;
892
- };
893
799
 
894
- interface IProps {
895
- slug: string;
896
- children: React.ReactNode;
800
+ interface AuthRequestParams {
801
+ orgId: string;
802
+ clientId: string;
803
+ redirect: {
804
+ success: string;
805
+ error: string;
806
+ };
807
+ }
808
+ interface AuthRequestResponse {
809
+ success: boolean;
810
+ data: {
811
+ redirectUrl: string;
812
+ };
813
+ message: string;
814
+ }
815
+ declare class AuthApi extends BaseApi {
816
+ constructor(config: Pick<IOsConfig, 'serverUrl' | 'version'> & {
817
+ sessionId?: string;
818
+ });
819
+ /**
820
+ * Initiate OAuth sign-in flow. Returns redirect URL to the auth provider.
821
+ * Uses /api/v1/auth/request (no 'public' prefix — auth endpoints are at the root).
822
+ */
823
+ requestAuth(params: AuthRequestParams): Promise<AuthRequestResponse>;
824
+ /** Fetch user profile with the given session ID (used after OAuth redirect or from storage). */
825
+ getProfile(sessionId: string, signal?: AbortSignal): Promise<IUser>;
897
826
  }
898
- /**
899
- * Conditional component that renders children only when the specified workspace feature is enabled.
900
- * Checks feature flags at the workspace level.
901
- *
902
- * @param props - Component props
903
- * @param props.slug - Feature flag slug/key to check
904
- * @param props.children - Content to render when feature is enabled
905
- *
906
- * @example
907
- * ```tsx
908
- * function PremiumFeature() {
909
- * return (
910
- * <WhenWorkspaceFeatureEnabled slug="premium-analytics">
911
- * <AnalyticsDashboard />
912
- * </WhenWorkspaceFeatureEnabled>
913
- * );
914
- * }
915
- * ```
916
- *
917
- * @example
918
- * ```tsx
919
- * // Multiple features
920
- * function FeatureContent() {
921
- * return (
922
- * <>
923
- * <WhenWorkspaceFeatureEnabled slug="feature-a">
924
- * <FeatureA />
925
- * </WhenWorkspaceFeatureEnabled>
926
- * <WhenWorkspaceFeatureEnabled slug="feature-b">
927
- * <FeatureB />
928
- * </WhenWorkspaceFeatureEnabled>
929
- * </>
930
- * );
931
- * }
932
- * ```
933
- */
934
- declare const WhenWorkspaceFeatureEnabled: {
935
- (props: IProps): react.ReactNode;
936
- displayName: string;
937
- };
938
- /**
939
- * Conditional component that renders children only when the specified workspace feature is disabled.
940
- * Checks feature flags at the workspace level.
941
- *
942
- * @param props - Component props
943
- * @param props.slug - Feature flag slug/key to check
944
- * @param props.children - Content to render when feature is disabled
945
- *
946
- * @example
947
- * ```tsx
948
- * function UpgradePrompt() {
949
- * return (
950
- * <WhenWorkspaceFeatureDisabled slug="premium-feature">
951
- * <UpgradeButton />
952
- * </WhenWorkspaceFeatureDisabled>
953
- * );
954
- * }
955
- * ```
956
- */
957
- declare const WhenWorkspaceFeatureDisabled: {
958
- (props: IProps): react.ReactNode;
959
- displayName: string;
960
- };
961
- /**
962
- * Conditional component that renders children only when the specified user feature is enabled.
963
- * Checks feature flags at the user level (from UserProvider).
964
- *
965
- * @param props - Component props
966
- * @param props.slug - Feature flag slug/key to check
967
- * @param props.children - Content to render when feature is enabled
968
- *
969
- * @example
970
- * ```tsx
971
- * function BetaFeature() {
972
- * return (
973
- * <WhenUserFeatureEnabled slug="beta-access">
974
- * <BetaContent />
975
- * </WhenUserFeatureEnabled>
976
- * );
977
- * }
978
- * ```
979
- *
980
- * @example
981
- * ```tsx
982
- * // Edge case: Feature not loaded yet
983
- * function FeatureContent() {
984
- * const { isLoading } = useUserFeatures();
985
- *
986
- * if (isLoading) return <Loading />;
987
- *
988
- * return (
989
- * <WhenUserFeatureEnabled slug="feature-x">
990
- * <FeatureX />
991
- * </WhenUserFeatureEnabled>
992
- * );
993
- * }
994
- * ```
995
- */
996
- declare const WhenUserFeatureEnabled: {
997
- (props: IProps): react.ReactNode;
998
- displayName: string;
999
- };
1000
- /**
1001
- * Conditional component that renders children only when the specified user feature is disabled.
1002
- * Checks feature flags at the user level (from UserProvider).
1003
- *
1004
- * @param props - Component props
1005
- * @param props.slug - Feature flag slug/key to check
1006
- * @param props.children - Content to render when feature is disabled
1007
- *
1008
- * @example
1009
- * ```tsx
1010
- * function UpgradePrompt() {
1011
- * return (
1012
- * <WhenUserFeatureDisabled slug="premium-access">
1013
- * <UpgradeButton />
1014
- * </WhenUserFeatureDisabled>
1015
- * );
1016
- * }
1017
- * ```
1018
- */
1019
- declare const WhenUserFeatureDisabled: {
1020
- (props: IProps): react.ReactNode;
1021
- displayName: string;
1022
- };
1023
827
 
1024
- interface IWhenSubscriptionProps {
1025
- /** Content to render when the condition is met (workspace has an active subscription). */
1026
- children: React.ReactNode;
1027
- /** Optional component/element to show while subscription is loading (e.g. <Skeleton />). */
1028
- loadingComponent?: React.ReactNode;
1029
- /** Optional component/element to show when condition is not met (e.g. <UpgradePrompt />). */
1030
- fallbackComponent?: React.ReactNode;
828
+ interface IScreenDetail {
829
+ title: string;
830
+ description: string;
1031
831
  }
1032
- /**
1033
- * Renders children only when the current workspace has an active subscription (any plan).
1034
- * Optionally pass loadingComponent (while loading) or fallbackComponent (when not subscribed).
1035
- * Must be used within SubscriptionContextProvider.
1036
- *
1037
- * @param props - Component props
1038
- * @param props.children - Content to render when subscribed
1039
- * @param props.loadingComponent - Optional component/element to show while loading
1040
- * @param props.fallbackComponent - Optional component/element to show when not subscribed
1041
- * @returns ReactNode - children when subscribed, loadingComponent when loading, fallbackComponent when not subscribed, or null
1042
- *
1043
- * @example
1044
- * ```tsx
1045
- * <WhenSubscription>
1046
- * <BillingSettings />
1047
- * </WhenSubscription>
1048
- * ```
1049
- *
1050
- * @example
1051
- * ```tsx
1052
- * <WhenSubscription
1053
- * loadingComponent={<Skeleton />}
1054
- * fallbackComponent={<UpgradePrompt />}
1055
- * >
1056
- * <BillingSettings />
1057
- * </WhenSubscription>
1058
- * ```
1059
- */
1060
- declare const WhenSubscription: {
1061
- (props: IWhenSubscriptionProps): react.ReactNode;
1062
- displayName: string;
1063
- };
1064
- /**
1065
- * Renders children only when the current workspace has no subscription (or no current workspace).
1066
- * Optionally pass loadingComponent (while loading) or fallbackComponent (when already subscribed).
1067
- * Must be used within SubscriptionContextProvider.
1068
- *
1069
- * @param props - Component props
1070
- * @param props.children - Content to render when not subscribed
1071
- * @param props.loadingComponent - Optional component/element to show while loading
1072
- * @param props.fallbackComponent - Optional component/element to show when already subscribed
1073
- * @returns ReactNode - children when not subscribed, loadingComponent when loading, fallbackComponent when subscribed, or null
1074
- *
1075
- * @example
1076
- * ```tsx
1077
- * <WhenNoSubscription>
1078
- * <UpgradePrompt />
1079
- * </WhenNoSubscription>
1080
- * ```
1081
- *
1082
- * @example
1083
- * ```tsx
1084
- * <WhenNoSubscription
1085
- * loadingComponent={<Spinner />}
1086
- * fallbackComponent={null}
1087
- * >
1088
- * <UpgradePrompt />
1089
- * </WhenNoSubscription>
1090
- * ```
1091
- */
1092
- declare const WhenNoSubscription: {
1093
- (props: IWhenSubscriptionProps): react.ReactNode;
1094
- displayName: string;
1095
- };
1096
- interface IWhenSubscriptionToPlansProps {
1097
- /** Plan slugs to match (e.g. ['pro', 'enterprise']). Matching is case-insensitive. */
1098
- plans: string[];
1099
- /** Content to render when the workspace is on one of the given plans. */
1100
- children: React.ReactNode;
1101
- /** Optional component/element to show while subscription is loading (e.g. <Skeleton />). */
1102
- loadingComponent?: React.ReactNode;
1103
- /** Optional component/element to show when not on a matching plan (e.g. <UpgradeToPro />). */
1104
- fallbackComponent?: React.ReactNode;
832
+ interface IBetaConfig extends IDocument {
833
+ name: string;
834
+ smallName: string;
835
+ description: string;
836
+ logoFallBack: string;
837
+ logo: string | IAsset;
838
+ privacyPolicy: string;
839
+ termsOfService: string;
840
+ enabled: boolean;
841
+ screen: {
842
+ register: IScreenDetail;
843
+ thankYou: IScreenDetail;
844
+ };
1105
845
  }
1106
- /**
1107
- * Renders children only when the current workspace is subscribed to one of the given plans.
1108
- * Matches by plan slug only. Optionally pass loadingComponent (while loading) or fallbackComponent (when not on a matching plan).
1109
- * Must be used within SubscriptionContextProvider.
1110
- *
1111
- * @param props - Component props
1112
- * @param props.plans - Plan slugs to match (e.g. ['pro', 'enterprise'])
1113
- * @param props.children - Content to render when on a matching plan
1114
- * @param props.loadingComponent - Optional component/element to show while loading
1115
- * @param props.fallbackComponent - Optional component/element to show when not on a matching plan
1116
- * @returns ReactNode - children when on a matching plan, loadingComponent when loading, fallbackComponent when not matching, or null
1117
- *
1118
- * @example
1119
- * ```tsx
1120
- * <WhenSubscriptionToPlans plans={['pro', 'enterprise']}>
1121
- * <AdvancedAnalytics />
1122
- * </WhenSubscriptionToPlans>
1123
- * ```
1124
- *
1125
- * @example
1126
- * ```tsx
1127
- * <WhenSubscriptionToPlans
1128
- * plans={['pro', 'enterprise']}
1129
- * loadingComponent={<Skeleton />}
1130
- * fallbackComponent={<UpgradeToPro />}
1131
- * >
1132
- * <AdvancedAnalytics />
1133
- * </WhenSubscriptionToPlans>
1134
- * ```
1135
- */
1136
- declare const WhenSubscriptionToPlans: {
1137
- (props: IWhenSubscriptionToPlansProps): react.ReactNode;
1138
- displayName: string;
1139
- };
1140
- interface IWhenTrialingProps {
1141
- /** Content to render when the condition is met. */
1142
- children: React.ReactNode;
1143
- /** Optional component/element to show while subscription is loading. */
1144
- loadingComponent?: React.ReactNode;
1145
- /** Optional component/element to show when condition is not met. */
1146
- fallbackComponent?: React.ReactNode;
846
+ interface ApiResponse {
847
+ status: string;
848
+ message: string;
849
+ data?: {
850
+ submissionId: string;
851
+ status: string;
852
+ };
853
+ code: string;
854
+ config?: IBetaConfig;
855
+ submissionId?: string;
1147
856
  }
1148
- /**
1149
- * Renders children only when the current workspace subscription is in trial (status === 'trialing').
1150
- * Must be used within SubscriptionContextProvider.
1151
- *
1152
- * @example
1153
- * ```tsx
1154
- * <WhenTrialing fallbackComponent={<NormalContent />}>
1155
- * <TrialBanner />
1156
- * </WhenTrialing>
1157
- * ```
1158
- */
1159
- declare const WhenTrialing: {
1160
- (props: IWhenTrialingProps): react.ReactNode;
1161
- displayName: string;
1162
- };
1163
- /**
1164
- * Renders children only when the current workspace subscription is NOT in trial.
1165
- * This includes: no subscription, active, canceled, past_due, etc.
1166
- * Must be used within SubscriptionContextProvider.
1167
- *
1168
- * @example
1169
- * ```tsx
1170
- * <WhenNotTrialing>
1171
- * <RegularPricingPage />
1172
- * </WhenNotTrialing>
1173
- * ```
1174
- */
1175
- declare const WhenNotTrialing: {
1176
- (props: IWhenTrialingProps): react.ReactNode;
1177
- displayName: string;
1178
- };
1179
- interface IWhenTrialEndingProps {
1180
- /** Content to render when trial is ending soon. */
1181
- children: React.ReactNode;
1182
- /** Optional component/element to show while subscription is loading. */
1183
- loadingComponent?: React.ReactNode;
1184
- /** Optional component/element to show when trial is not ending soon. */
1185
- fallbackComponent?: React.ReactNode;
1186
- /** Number of days threshold to consider "ending soon". Defaults to 3. */
1187
- daysThreshold?: number;
857
+ declare class BetaForm {
858
+ private api;
859
+ private orgId;
860
+ constructor(config: IOsConfig);
861
+ fetchConfig(): Promise<IBetaConfig>;
862
+ submitBetaUser(formData: {
863
+ name: string;
864
+ email: string;
865
+ context?: {
866
+ country?: string;
867
+ language?: string;
868
+ timezone?: string;
869
+ currency?: string;
870
+ };
871
+ }): Promise<ApiResponse>;
1188
872
  }
1189
- /**
1190
- * Renders children only when the subscription is trialing AND the trial ends within
1191
- * the given threshold (default 3 days). Useful for showing urgent upgrade prompts.
1192
- * Must be used within SubscriptionContextProvider.
1193
- *
1194
- * @example
1195
- * ```tsx
1196
- * <WhenTrialEnding daysThreshold={5}>
1197
- * <UpgradeBanner />
1198
- * </WhenTrialEnding>
1199
- * ```
1200
- */
1201
- declare const WhenTrialEnding: {
1202
- (props: IWhenTrialEndingProps): react.ReactNode;
1203
- displayName: string;
1204
- };
1205
873
 
1206
- interface IWhenQuotaProps {
1207
- /** Quota slug to check (e.g. 'api_calls', 'emails', 'storage'). */
1208
- slug: string;
1209
- /** Content to render when the condition is met. */
1210
- children: React.ReactNode;
1211
- /** Optional component/element to show while quota usage is loading (e.g. <Skeleton />). */
1212
- loadingComponent?: React.ReactNode;
1213
- /** Optional component/element to show when condition is not met (e.g. <UpgradePrompt />). */
1214
- fallbackComponent?: React.ReactNode;
1215
- }
1216
- interface IWhenQuotaThresholdProps extends IWhenQuotaProps {
1217
- /** Usage percentage threshold (0-100). Children render when usage >= this percentage. */
1218
- threshold: number;
874
+ declare class PushApi extends BaseApi {
875
+ constructor(config: Pick<IBaseApiConfig, 'serverUrl' | 'version' | 'orgId' | 'sessionId'>);
876
+ getVapidPublicKey(): Promise<{
877
+ publicKey: string;
878
+ }>;
879
+ subscribe(subscription: {
880
+ endpoint: string;
881
+ keys: {
882
+ p256dh: string;
883
+ auth: string;
884
+ };
885
+ }, userAgent: string): Promise<void>;
886
+ unsubscribe(endpoint: string): Promise<void>;
1219
887
  }
888
+
1220
889
  /**
1221
- * Renders children only when the specified quota has remaining units (available > 0).
1222
- * Must be used within QuotaUsageContextProvider (included in SaaSOSProvider by default).
1223
- *
1224
- * @example
1225
- * ```tsx
1226
- * <WhenQuotaAvailable slug="api_calls">
1227
- * <MakeApiCallButton />
1228
- * </WhenQuotaAvailable>
1229
- * ```
1230
- *
1231
- * @example
1232
- * ```tsx
1233
- * <WhenQuotaAvailable
1234
- * slug="emails"
1235
- * loadingComponent={<Skeleton />}
1236
- * fallbackComponent={<p>Email quota exhausted. <UpgradeLink /></p>}
1237
- * >
1238
- * <SendEmailButton />
1239
- * </WhenQuotaAvailable>
1240
- * ```
890
+ * Centralized API client for organization (OS) settings.
891
+ * Extends BaseApi for shared URL/auth/request handling.
1241
892
  */
1242
- declare const WhenQuotaAvailable: {
1243
- (props: IWhenQuotaProps): react.ReactNode;
1244
- displayName: string;
1245
- };
893
+
894
+ declare class SettingsApi extends BaseApi {
895
+ constructor(config: IOsConfig & {
896
+ sessionId?: string;
897
+ });
898
+ getSettings(signal?: AbortSignal): Promise<ISettings>;
899
+ }
900
+
1246
901
  /**
1247
- * Renders children only when the specified quota is fully consumed (available <= 0).
1248
- * Must be used within QuotaUsageContextProvider (included in SaaSOSProvider by default).
1249
- *
1250
- * @example
1251
- * ```tsx
1252
- * <WhenQuotaExhausted slug="api_calls">
1253
- * <UpgradePrompt message="You've used all your API calls this month." />
1254
- * </WhenQuotaExhausted>
1255
- * ```
1256
- *
1257
- * @example
1258
- * ```tsx
1259
- * <WhenQuotaExhausted
1260
- * slug="storage"
1261
- * loadingComponent={<Spinner />}
1262
- * fallbackComponent={null}
1263
- * >
1264
- * <StorageFullBanner />
1265
- * </WhenQuotaExhausted>
1266
- * ```
1267
- */
1268
- declare const WhenQuotaExhausted: {
1269
- (props: IWhenQuotaProps): react.ReactNode;
1270
- displayName: string;
1271
- };
1272
- /**
1273
- * Renders children only when the specified quota is in overage (consumed > included).
1274
- * Must be used within QuotaUsageContextProvider (included in SaaSOSProvider by default).
1275
- *
1276
- * @example
1277
- * ```tsx
1278
- * <WhenQuotaOverage slug="api_calls">
1279
- * <OverageBillingWarning />
1280
- * </WhenQuotaOverage>
1281
- * ```
1282
- *
1283
- * @example
1284
- * ```tsx
1285
- * <WhenQuotaOverage
1286
- * slug="emails"
1287
- * loadingComponent={<Skeleton />}
1288
- * fallbackComponent={null}
1289
- * >
1290
- * <p>You are being billed for overage usage.</p>
1291
- * </WhenQuotaOverage>
1292
- * ```
1293
- */
1294
- declare const WhenQuotaOverage: {
1295
- (props: IWhenQuotaProps): react.ReactNode;
1296
- displayName: string;
1297
- };
1298
- /**
1299
- * Renders children when the specified quota's usage percentage reaches or exceeds the threshold.
1300
- * Usage percentage = (consumed / included) * 100. If included is 0 and consumed > 0, treats as 100%.
1301
- * Must be used within QuotaUsageContextProvider (included in SaaSOSProvider by default).
1302
- *
1303
- * @example
1304
- * ```tsx
1305
- * <WhenQuotaThreshold slug="api_calls" threshold={80}>
1306
- * <p>Warning: You've used over 80% of your API calls.</p>
1307
- * </WhenQuotaThreshold>
1308
- * ```
1309
- *
1310
- * @example
1311
- * ```tsx
1312
- * <WhenQuotaThreshold
1313
- * slug="storage"
1314
- * threshold={90}
1315
- * loadingComponent={<Spinner />}
1316
- * fallbackComponent={null}
1317
- * >
1318
- * <StorageWarningBanner />
1319
- * </WhenQuotaThreshold>
1320
- * ```
1321
- */
1322
- declare const WhenQuotaThreshold: {
1323
- (props: IWhenQuotaThresholdProps): react.ReactNode;
1324
- displayName: string;
1325
- };
1326
-
1327
- /**
1328
- * Value provided by SubscriptionContext and returned by useSubscriptionContext.
1329
- */
1330
- interface SubscriptionContextValue {
1331
- /** Current subscription response for the current workspace, or null if none or not loaded. */
1332
- response: ISubscriptionResponse | null;
1333
- /** True while subscription is being fetched. */
1334
- loading: boolean;
1335
- /** Error message if the last fetch failed, or null. */
1336
- error: string | null;
1337
- /** Refetch subscription for the current workspace. Call after plan change (e.g. upgrade) or when subscription was updated elsewhere. */
1338
- refetch: () => Promise<void>;
1339
- }
1340
-
1341
- /**
1342
- * Provides subscription data for the current workspace to subscription gate components.
1343
- * Fetches when workspace changes; refetches when subscription is invalidated (e.g. after plan change).
1344
- * Must wrap (or be ancestor of) any component that uses WhenSubscription, WhenNoSubscription,
1345
- * WhenSubscriptionToPlans, or useSubscriptionContext. Included in SaaSOSProvider by default.
1346
- *
1347
- * @param props - Component props
1348
- * @param props.children - React tree that may use subscription gates or useSubscriptionContext
1349
- * @returns Provider element that supplies subscription context to descendants
1350
- */
1351
- declare const SubscriptionContextProvider: react__default.FC<{
1352
- children: ReactNode;
1353
- }>;
1354
- /**
1355
- * Returns subscription data for the current workspace. Must be used within SubscriptionContextProvider.
1356
- *
1357
- * @returns SubscriptionContextValue - { response, loading, refetch }
1358
- * @throws Error if used outside SubscriptionContextProvider
1359
- */
1360
- declare function useSubscriptionContext(): SubscriptionContextValue;
1361
-
1362
- /**
1363
- * Notify all subscribers to refetch subscription (e.g. after update/cancel/resume).
1364
- * Called internally by useUpdateSubscription, useCancelSubscription, useResumeSubscription on success.
1365
- */
1366
- declare function invalidateSubscription(): void;
1367
-
1368
- /**
1369
- * Value provided by QuotaUsageContext and returned by useQuotaUsageContext.
1370
- */
1371
- interface QuotaUsageContextValue {
1372
- /** Current quota usage statuses keyed by slug, or null if not loaded. */
1373
- quotas: Record<string, IQuotaUsageStatus> | null;
1374
- /** True while quota usage is being fetched. */
1375
- loading: boolean;
1376
- /** Error message if the last fetch failed, or null. */
1377
- error: string | null;
1378
- /** Refetch all quota usage for the current workspace. Call after recording usage or when usage was updated elsewhere. */
1379
- refetch: () => Promise<void>;
1380
- }
1381
-
1382
- /**
1383
- * Provides quota usage data for the current workspace to quota gate components.
1384
- * Fetches when workspace changes; refetches when quota usage is invalidated (e.g. after recording usage).
1385
- * Must wrap (or be ancestor of) any component that uses WhenQuotaAvailable, WhenQuotaExhausted,
1386
- * WhenQuotaOverage, WhenQuotaThreshold, or useQuotaUsageContext. Included in SaaSOSProvider by default.
1387
- *
1388
- * @param props - Component props
1389
- * @param props.children - React tree that may use quota gates or useQuotaUsageContext
1390
- * @returns Provider element that supplies quota usage context to descendants
1391
- */
1392
- declare const QuotaUsageContextProvider: react__default.FC<{
1393
- children: ReactNode;
1394
- }>;
1395
- /**
1396
- * Returns quota usage data for the current workspace. Must be used within QuotaUsageContextProvider.
1397
- *
1398
- * @returns QuotaUsageContextValue - { quotas, loading, refetch }
1399
- * @throws Error if used outside QuotaUsageContextProvider
1400
- */
1401
- declare function useQuotaUsageContext(): QuotaUsageContextValue;
1402
-
1403
- /**
1404
- * Notify all subscribers to refetch quota usage (e.g. after recording usage).
1405
- * Called internally by useRecordUsage on success.
1406
- */
1407
- declare function invalidateQuotaUsage(): void;
1408
-
1409
- type WorkspaceSettingsSection = 'profile' | 'general' | 'users' | 'subscription' | 'usage' | 'features' | 'notifications' | 'danger';
1410
-
1411
- declare function useSaaSAuth(): {
1412
- signIn: () => Promise<void>;
1413
- signOut: () => Promise<void>;
1414
- openWorkspaceSettings: (section?: WorkspaceSettingsSection) => void;
1415
- isAuthenticated: boolean;
1416
- isLoading: boolean;
1417
- isRedirecting: boolean;
1418
- user: AuthUser | undefined;
1419
- session: AuthSession | null;
1420
- status: AuthStatus;
1421
- };
1422
-
1423
- /**
1424
- * Hook to access OS (organization) state (serverUrl, version, orgId, auth, settings).
1425
- * Prefer useSaaSSettings() when you only need settings.
1426
- */
1427
- declare function useSaaSOs(): IOsState;
1428
- /**
1429
- * Hook to access organization settings from the OS context.
1430
- * Automatically fetches settings when OS config is ready.
1431
- *
1432
- * @returns An object containing:
1433
- * - `settings`: Organization settings object (null if not loaded)
1434
- * - `getSettings(signal?)`: Function to manually fetch settings (supports AbortSignal)
1435
- *
1436
- * @example
1437
- * ```tsx
1438
- * function SettingsDisplay() {
1439
- * const { settings } = useSaaSSettings();
1440
- *
1441
- * if (!settings) return <Loading />;
1442
- *
1443
- * return (
1444
- * <div>
1445
- * <p>Organization: {settings.name}</p>
1446
- * <p>Theme: {settings.theme}</p>
1447
- * </div>
1448
- * );
1449
- * }
1450
- * ```
1451
- *
1452
- * @example
1453
- * ```tsx
1454
- * // Manual fetch with abort signal
1455
- * function SettingsLoader() {
1456
- * const { getSettings } = useSaaSSettings();
1457
- *
1458
- * useEffect(() => {
1459
- * const controller = new AbortController();
1460
- * getSettings(controller.signal);
1461
- *
1462
- * return () => controller.abort();
1463
- * }, [getSettings]);
1464
- * }
1465
- * ```
1466
- */
1467
- declare function useSaaSSettings(): {
1468
- settings: ISettings | null | undefined;
1469
- getSettings: (signal?: AbortSignal) => Promise<ISettings | null>;
1470
- };
1471
-
1472
- interface UserContextValue {
1473
- attributes: Record<string, string | number | boolean>;
1474
- features: Record<string, boolean>;
1475
- isLoading: boolean;
1476
- error: Error | null;
1477
- updateAttributes: (updates: Record<string, string | number | boolean>) => Promise<IUser>;
1478
- updateAttribute: (attributeKey: string, value: string | number | boolean) => Promise<IUser>;
1479
- refreshAttributes: () => Promise<void>;
1480
- refreshFeatures: () => Promise<void>;
1481
- }
1482
-
1483
- /**
1484
- * Base API client for SDK endpoints.
1485
- * All domain API classes (WorkspaceApi, UserApi, etc.) extend this to share
1486
- * URL building, auth headers, and request/response handling.
902
+ * API client for user attributes and features.
903
+ * Extends BaseApi for shared URL/auth/request handling.
1487
904
  */
1488
905
 
1489
- interface IBaseApiConfig {
1490
- serverUrl: string;
1491
- version: ApiVersion;
1492
- orgId?: string;
1493
- /** When true, ensureReady() also requires orgId. Used by WorkspaceApi and SettingsApi. */
1494
- requireOrgId?: boolean;
1495
- /** API path segment after version (default 'public'). e.g. 'public' => .../v1/public, 'beta' => .../v1/beta */
1496
- basePath?: string;
1497
- }
1498
- /**
1499
- * Base class for SDK API clients.
1500
- * Provides:
1501
- * - baseUrl: `${serverUrl}/api/${version}/public`
1502
- * - getAuthHeaders()
1503
- * - fetchJson<T>(path, init, errorMessage): GET/POST/etc. with handleApiResponse
1504
- * - fetchResponse(path, init): raw Response for custom parsing (e.g. non-JSON or custom error handling)
1505
- */
1506
- declare abstract class BaseApi {
1507
- protected readonly serverUrl: string;
1508
- protected readonly version: ApiVersion;
1509
- protected readonly orgId: string | undefined;
1510
- private readonly requireOrgId;
1511
- private readonly basePath;
1512
- constructor(config: IBaseApiConfig);
1513
- /** Throws if config is not ready for API calls. Called automatically by fetchJson/fetchResponse. */
1514
- protected ensureReady(): void;
1515
- /** Base URL: ${serverUrl}/api/${version}/${basePath} */
1516
- protected get baseUrl(): string;
1517
- /** Auth headers (x-session-id). Subclasses can override to add more. */
1518
- protected getAuthHeaders(): Record<string, string>;
1519
- /** Build full URL from path (path can be "workspaces" or "/workspaces"). */
1520
- protected url(path: string): string;
1521
- /**
1522
- * Execute request and parse JSON with handleApiResponse (throws on !response.ok).
1523
- * Use for standard JSON APIs.
1524
- */
1525
- protected fetchJson<T>(path: string, init?: RequestInit, errorMessage?: string): Promise<T>;
1526
- /**
1527
- * Execute request and return raw Response (for custom parsing or error handling).
1528
- * Caller is responsible for checking response.ok and parsing body.
1529
- */
1530
- protected fetchResponse(path: string, init?: RequestInit): Promise<Response>;
1531
- }
1532
-
1533
- /**
1534
- * Centralized API client for user attributes and features.
1535
- * Extends BaseApi for shared URL/auth/request handling.
1536
- */
1537
906
  declare class UserApi extends BaseApi {
1538
- constructor(config: Pick<IOsConfig, 'serverUrl' | 'version'>);
907
+ constructor(config: Pick<IOsConfig, 'serverUrl' | 'version'> & {
908
+ sessionId?: string;
909
+ });
1539
910
  getAttributes(signal?: AbortSignal): Promise<Record<string, string | number | boolean>>;
1540
911
  updateAttributes(updates: Record<string, string | number | boolean>): Promise<IUser>;
1541
912
  updateAttribute(attributeKey: string, value: string | number | boolean): Promise<IUser>;
1542
913
  getFeatures(signal?: AbortSignal): Promise<Record<string, boolean>>;
1543
914
  }
1544
915
 
1545
- /**
1546
- * Hook to access user attributes from the UserProvider.
1547
- * Must be used within a UserProvider component.
1548
- *
1549
- * @returns User context object containing:
1550
- * - `attributes`: Record of user attribute key-value pairs
1551
- * - `isLoading`: Boolean indicating if attributes are being loaded
1552
- * - `error`: Error message string (null if no error)
1553
- * - `refreshAttributes()`: Function to manually refresh attributes
1554
- *
1555
- * @throws {Error} If used outside of UserProvider
1556
- *
1557
- * @example
1558
- * ```tsx
1559
- * function UserProfile() {
1560
- * const { attributes, isLoading } = useUserAttributes();
1561
- *
1562
- * if (isLoading) return <Loading />;
1563
- *
1564
- * return (
1565
- * <div>
1566
- * <p>Plan: {attributes?.plan}</p>
1567
- * <p>Company: {attributes?.company}</p>
1568
- * </div>
1569
- * );
1570
- * }
1571
- * ```
1572
- */
1573
- declare function useUserAttributes(): UserContextValue;
1574
- /**
1575
- * Hook to access user feature flags from the UserProvider.
1576
- * Must be used within a UserProvider component.
1577
- *
1578
- * @returns An object containing:
1579
- * - `features`: Record of feature flag key-value pairs (boolean values)
1580
- * - `isLoading`: Boolean indicating if features are being loaded
1581
- * - `error`: Error message string (null if no error)
1582
- * - `refreshFeatures()`: Function to manually refresh features
1583
- * - `isFeatureEnabled(featureId)`: Function to check if a specific feature is enabled
1584
- *
1585
- * @throws {Error} If used outside of UserProvider
1586
- *
1587
- * @example
1588
- * ```tsx
1589
- * function FeatureContent() {
1590
- * const { isFeatureEnabled, isLoading } = useUserFeatures();
1591
- *
1592
- * if (isLoading) return <Loading />;
1593
- *
1594
- * if (isFeatureEnabled('premium-feature')) {
1595
- * return <PremiumFeature />;
1596
- * }
1597
- *
1598
- * return <BasicFeature />;
1599
- * }
1600
- * ```
1601
- *
1602
- * @example
1603
- * ```tsx
1604
- * // Check multiple features
1605
- * function Dashboard() {
1606
- * const { isFeatureEnabled } = useUserFeatures();
1607
- *
1608
- * return (
1609
- * <div>
1610
- * {isFeatureEnabled('analytics') && <Analytics />}
1611
- * {isFeatureEnabled('reports') && <Reports />}
1612
- * </div>
1613
- * );
1614
- * }
1615
- * ```
1616
- */
1617
- declare function useUserFeatures(): {
1618
- features: Record<string, boolean>;
1619
- isLoading: boolean;
1620
- error: Error | null;
1621
- refreshFeatures: () => Promise<void>;
1622
- isFeatureEnabled: (featureId: string) => boolean;
1623
- };
1624
-
1625
916
  declare class WorkspaceApi extends BaseApi {
1626
- constructor(config: IOsConfig);
917
+ constructor(config: IOsConfig & {
918
+ sessionId?: string;
919
+ });
1627
920
  getWorkspaces(): Promise<IWorkspace[]>;
1628
921
  createWorkspace(data: {
1629
922
  name: string;
@@ -1720,997 +1013,66 @@ declare class WorkspaceApi extends BaseApi {
1720
1013
  * Create a Stripe Customer Portal session for managing payment methods, invoices, etc.
1721
1014
  * Returns the portal URL — redirect user to it.
1722
1015
  */
1723
- createBillingPortalSession(workspaceId: string, returnUrl?: string): Promise<{
1724
- url: string;
1725
- }>;
1726
- /**
1727
- * List invoices for a workspace subscription
1728
- * @param workspaceId - The workspace ID
1729
- * @param limit - Number of invoices to return (default: 10)
1730
- * @param startingAfter - Invoice ID to start after (for pagination)
1731
- * @returns List of invoices with pagination info
1732
- */
1733
- listInvoices(workspaceId: string, limit?: number, startingAfter?: string): Promise<IInvoiceListResponse>;
1734
- /**
1735
- * Get a single invoice by ID
1736
- * @param workspaceId - The workspace ID
1737
- * @param invoiceId - The invoice ID
1738
- * @returns Invoice details
1739
- */
1740
- getInvoice(workspaceId: string, invoiceId: string): Promise<IInvoiceResponse>;
1741
- /**
1742
- * Cancel subscription at the end of the current billing period
1743
- * Sets cancelAtPeriodEnd: true - subscription remains active until period ends
1744
- * @param workspaceId - The workspace ID
1745
- * @returns Updated subscription with cancelAtPeriodEnd and stripeCurrentPeriodEnd
1746
- */
1747
- cancelSubscriptionAtPeriodEnd(workspaceId: string): Promise<ISubscriptionResponse>;
1748
- /**
1749
- * Resume a subscription that was scheduled for cancellation
1750
- * Sets cancelAtPeriodEnd: false - subscription will continue after period ends
1751
- * @param workspaceId - The workspace ID
1752
- * @returns Updated subscription with cancelAtPeriodEnd set to false
1753
- */
1754
- resumeSubscription(workspaceId: string): Promise<ISubscriptionResponse>;
1755
- /**
1756
- * Record quota usage for a workspace
1757
- * @param workspaceId - The workspace ID
1758
- * @param request - Usage request with quotaSlug, quantity, and optional metadata/source
1759
- * @returns Usage result with consumed/included/available/overage
1760
- */
1761
- recordUsage(workspaceId: string, request: IRecordUsageRequest): Promise<IRecordUsageResponse>;
1762
- /**
1763
- * Get usage status for a single quota
1764
- * @param workspaceId - The workspace ID
1765
- * @param quotaSlug - The quota slug to check
1766
- * @returns Quota usage status with consumed/included/available/overage/hasOverage
1767
- */
1768
- getQuotaUsageStatus(workspaceId: string, quotaSlug: string): Promise<IQuotaUsageStatusResponse>;
1769
- /**
1770
- * Get usage status for all quotas in the workspace's current plan
1771
- * @param workspaceId - The workspace ID
1772
- * @returns All quota usage statuses keyed by quota slug
1773
- */
1774
- getAllQuotaUsage(workspaceId: string): Promise<IAllQuotaUsageResponse>;
1775
- /**
1776
- * Get paginated usage logs for a workspace
1777
- * @param workspaceId - The workspace ID
1778
- * @param query - Optional filters: quotaSlug, from, to, source, page, limit
1779
- * @returns Paginated usage log entries
1780
- */
1781
- getUsageLogs(workspaceId: string, query?: IUsageLogsQuery): Promise<IUsageLogsResponse>;
1782
- }
1783
-
1784
- declare const useSaaSWorkspaces: () => {
1785
- workspaces: IWorkspace[];
1786
- loading: boolean;
1787
- error: string | null;
1788
- fetchWorkspaces: () => Promise<void>;
1789
- refreshWorkspaces: () => Promise<void>;
1790
- refreshing: boolean;
1791
- currentWorkspace: IWorkspace | null;
1792
- setCurrentWorkspace: (ws: IWorkspace, options?: {
1793
- forceEmit?: boolean;
1794
- }) => void;
1795
- switchToWorkspace: (ws: IWorkspace, options?: {
1796
- forceEmit?: boolean;
1797
- }) => Promise<void>;
1798
- resetCurrentWorkspace: () => void;
1799
- createWorkspace: (name: string, image?: string) => Promise<void>;
1800
- allFeatures: IWorkspaceFeature[];
1801
- getFeatures: () => Promise<IWorkspaceFeature[] | null>;
1802
- updateFeature: (workspaceId: string, key: string, value: boolean) => Promise<IWorkspace>;
1803
- getWorkspace: (workspaceId: string) => Promise<IWorkspace>;
1804
- updateWorkspace: (ws: IWorkspace, _data: Partial<IWorkspace>) => Promise<void>;
1805
- getUsers: (workspaceId: string) => Promise<IWorkspaceUser[]>;
1806
- addUser: (workspaceId: string, email: string, role: string) => Promise<{
1807
- userId: string;
1808
- workspace: IWorkspace;
1809
- message: string;
1810
- }>;
1811
- removeUser: (workspaceId: string, userId: string) => Promise<{
1812
- userId: string;
1813
- workspace: IWorkspace;
1814
- message: string;
1815
- }>;
1816
- updateUser: (workspaceId: string, userId: string, config: Partial<IWorkspaceUser>) => Promise<{
1817
- userId: string;
1818
- workspace: IWorkspace;
1819
- message: string;
1820
- }>;
1821
- getProfile: () => Promise<IUser>;
1822
- updateUserProfile: (config: Partial<IUser>) => Promise<IUser>;
1823
- deleteWorkspace: (workspaceId: string) => Promise<{
1824
- success: boolean;
1825
- }>;
1826
- switching: boolean;
1827
- switchingToId: string | null;
1828
- };
1829
-
1830
- declare function WorkspaceSwitcher(props: {
1831
- trigger: (isLoading: boolean, currentWorkspace: IWorkspace | null) => ReactNode;
1832
- }): react_jsx_runtime.JSX.Element;
1833
-
1834
- /**
1835
- * Hook to get public plans by slug (no auth required).
1836
- * Returns items (features, limits, quotas) and plans (with pricing).
1837
- * Uses orgId from SaaSOSProvider - must be inside provider.
1838
- *
1839
- * @param slug - Plan group slug (e.g. 'main-pricing', 'enterprise')
1840
- * @returns { items, plans, loading, error, refetch }
1841
- */
1842
- declare const usePublicPlans: (slug: string) => {
1843
- items: IPublicPlanItem[];
1844
- plans: IPublicPlanVersion[];
1845
- notes: string | undefined;
1846
- loading: boolean;
1847
- error: string | null;
1848
- refetch: () => Promise<void>;
1849
- };
1850
- /**
1851
- * Hook to get a single plan group version by ID (no auth required).
1852
- * Use this for public pricing pages when you have the groupVersionId (e.g. from config or URL).
1853
- *
1854
- * @param groupVersionId - The plan group version ID to fetch. Pass null/undefined to disable fetching.
1855
- * @returns An object containing:
1856
- * - `planGroupVersion`: Plan group version data (null if not loaded)
1857
- * - `loading`: Boolean indicating if data is being fetched
1858
- * - `error`: Error message string (null if no error)
1859
- * - `refetch()`: Function to manually refetch
1860
- *
1861
- * @example
1862
- * ```tsx
1863
- * function PublicPricingPage() {
1864
- * const groupVersionId = 'your-plan-group-version-id'; // from config or URL
1865
- * const { planGroupVersion, loading } = usePublicPlanGroupVersion(groupVersionId);
1866
- *
1867
- * if (loading) return <Loading />;
1868
- * if (!planGroupVersion) return null;
1869
- *
1870
- * const plans = Array.isArray(planGroupVersion.planVersionIds)
1871
- * ? planGroupVersion.planVersionIds.filter((p): p is IPlanVersionWithPlan => typeof p !== 'string')
1872
- * : [];
1873
- *
1874
- * return (
1875
- * <div>
1876
- * {plans.map(plan => <PlanCard key={plan._id} plan={plan} />)}
1877
- * </div>
1878
- * );
1879
- * }
1880
- * ```
1881
- */
1882
- declare const usePublicPlanGroupVersion: (groupVersionId: string | null | undefined) => {
1883
- planGroupVersion: IPlanGroupVersion | null;
1884
- loading: boolean;
1885
- error: string | null;
1886
- refetch: () => Promise<void>;
1887
- };
1888
- /**
1889
- * Hook to get and manage the current subscription for a workspace.
1890
- * Automatically fetches subscription when workspaceId changes.
1891
- *
1892
- * @param workspaceId - The workspace ID to get subscription for. Can be null/undefined to disable fetching.
1893
- * @returns An object containing:
1894
- * - `subscription`: Current subscription data (null if no subscription or not loaded)
1895
- * - `loading`: Boolean indicating if subscription is being fetched
1896
- * - `error`: Error message string (null if no error)
1897
- * - `refetch()`: Function to manually refetch the subscription
1898
- *
1899
- * @example
1900
- * ```tsx
1901
- * function SubscriptionStatus() {
1902
- * const { currentWorkspace } = useSaaSWorkspaces();
1903
- * const { subscription, loading, error } = useSubscription(currentWorkspace?._id);
1904
- *
1905
- * if (loading) return <Loading />;
1906
- * if (error) return <Error message={error} />;
1907
- * if (!subscription) return <p>No active subscription</p>;
1908
- *
1909
- * return <p>Plan: {subscription.plan.name}</p>;
1910
- * }
1911
- * ```
1912
- *
1913
- * @example
1914
- * ```tsx
1915
- * // Edge case: Workspace ID changes
1916
- * function SubscriptionComponent({ workspaceId }) {
1917
- * const { subscription, refetch } = useSubscription(workspaceId);
1918
- *
1919
- * // Subscription automatically refetches when workspaceId changes
1920
- * // Use refetch() to manually refresh after mutations
1921
- * return <SubscriptionDetails subscription={subscription} />;
1922
- * }
1923
- * ```
1924
- */
1925
- declare const useSubscription: (workspaceId: string | null | undefined) => {
1926
- subscription: ISubscriptionResponse | null;
1927
- loading: boolean;
1928
- error: string | null;
1929
- refetch: () => Promise<void>;
1930
- };
1931
- /**
1932
- * Hook to get the plan group for a workspace.
1933
- * Returns the plan group containing the current plan if subscription exists,
1934
- * otherwise returns the latest published group.
1935
- * Automatically fetches when workspaceId or groupVersionId changes.
1936
- *
1937
- * @param workspaceId - The workspace ID to get plan group for. Can be null/undefined to disable fetching.
1938
- * @param groupVersionId - Optional: specific group version ID to fetch (for viewing historical versions)
1939
- * @returns An object containing:
1940
- * - `planGroup`: Plan group data (null if not loaded)
1941
- * - `loading`: Boolean indicating if plan group is being fetched
1942
- * - `error`: Error message string (null if no error)
1943
- * - `refetch()`: Function to manually refetch the plan group
1944
- *
1945
- * @example
1946
- * ```tsx
1947
- * function PlanGroupDisplay() {
1948
- * const { currentWorkspace } = useSaaSWorkspaces();
1949
- * const { planGroup, loading } = usePlanGroup(currentWorkspace?._id);
1950
- *
1951
- * if (loading) return <Loading />;
1952
- * if (!planGroup) return <p>No plan group available</p>;
1953
- *
1954
- * return (
1955
- * <div>
1956
- * <h3>{planGroup.group.name}</h3>
1957
- * {planGroup.plans.map(plan => (
1958
- * <PlanCard key={plan._id} plan={plan} />
1959
- * ))}
1960
- * </div>
1961
- * );
1962
- * }
1963
- * ```
1964
- *
1965
- * @example
1966
- * ```tsx
1967
- * // Fetch specific version for comparison
1968
- * function PlanVersionComparison() {
1969
- * const { currentWorkspace } = useSaaSWorkspaces();
1970
- * const current = usePlanGroup(currentWorkspace?._id);
1971
- * const previous = usePlanGroup(currentWorkspace?._id, 'previous-version-id');
1972
- *
1973
- * return <ComparePlans current={current.planGroup} previous={previous.planGroup} />;
1974
- * }
1975
- * ```
1976
- */
1977
- declare const usePlanGroup: (workspaceId: string | null | undefined, groupVersionId?: string | null) => {
1978
- planGroup: IPlanGroupResponse | null;
1979
- loading: boolean;
1980
- error: string | null;
1981
- refetch: () => Promise<void>;
1982
- };
1983
- /**
1984
- * Hook to get all available versions of a plan group for a workspace.
1985
- * Shows current version and available newer versions for upgrade paths.
1986
- * Automatically fetches when workspaceId changes.
1987
- *
1988
- * @param workspaceId - The workspace ID to get plan group versions for. Can be null/undefined to disable fetching.
1989
- * @returns An object containing:
1990
- * - `versions`: Plan group versions response with currentVersion and availableVersions
1991
- * - `loading`: Boolean indicating if versions are being fetched
1992
- * - `error`: Error message string (null if no error)
1993
- * - `refetch()`: Function to manually refetch the versions
1994
- *
1995
- * @example
1996
- * ```tsx
1997
- * function UpgradeOptions() {
1998
- * const { currentWorkspace } = useSaaSWorkspaces();
1999
- * const { versions, loading } = usePlanGroupVersions(currentWorkspace?._id);
2000
- *
2001
- * if (loading) return <Loading />;
2002
- *
2003
- * return (
2004
- * <div>
2005
- * <p>Current: {versions?.currentVersion.name}</p>
2006
- * <h4>Available Upgrades:</h4>
2007
- * {versions?.availableVersions.map(version => (
2008
- * <UpgradeCard key={version._id} version={version} />
2009
- * ))}
2010
- * </div>
2011
- * );
2012
- * }
2013
- * ```
2014
- */
2015
- declare const usePlanGroupVersions: (workspaceId: string | null | undefined) => {
2016
- versions: IPlanGroupVersionsResponse | null;
2017
- loading: boolean;
2018
- error: string | null;
2019
- refetch: () => Promise<void>;
2020
- };
2021
- /**
2022
- * Hook to create a checkout session for a new subscription.
2023
- * Returns a function to initiate the checkout process.
2024
- *
2025
- * @param workspaceId - The workspace ID to create checkout session for. Can be null/undefined.
2026
- * @returns An object containing:
2027
- * - `createCheckoutSession(request)`: Function to create checkout session (throws if workspaceId is null)
2028
- * - `loading`: Boolean indicating if checkout session is being created
2029
- * - `error`: Error message string (null if no error)
2030
- *
2031
- * @example
2032
- * ```tsx
2033
- * function SubscribeButton({ planVersionId }) {
2034
- * const { currentWorkspace } = useSaaSWorkspaces();
2035
- * const { createCheckoutSession, loading } = useCreateCheckoutSession(currentWorkspace?._id);
2036
- *
2037
- * const handleSubscribe = async () => {
2038
- * try {
2039
- * const result = await createCheckoutSession({
2040
- * planVersionId,
2041
- * successUrl: window.location.href,
2042
- * cancelUrl: window.location.href,
2043
- * });
2044
- * // Redirect to checkout
2045
- * window.location.href = result.checkoutUrl;
2046
- * } catch (error) {
2047
- * console.error('Failed to create checkout:', error);
2048
- * }
2049
- * };
2050
- *
2051
- * return (
2052
- * <button onClick={handleSubscribe} disabled={loading}>
2053
- * {loading ? 'Loading...' : 'Subscribe'}
2054
- * </button>
2055
- * );
2056
- * }
2057
- * ```
2058
- *
2059
- * @example
2060
- * ```tsx
2061
- * // Edge case: Workspace ID not available
2062
- * function SubscribeButton() {
2063
- * const { currentWorkspace } = useSaaSWorkspaces();
2064
- * const { createCheckoutSession } = useCreateCheckoutSession(currentWorkspace?._id);
2065
- *
2066
- * const handleSubscribe = async () => {
2067
- * try {
2068
- * await createCheckoutSession({ planVersionId: 'plan-123' });
2069
- * } catch (error) {
2070
- * // Error: "Workspace ID is required"
2071
- * alert('Please select a workspace first');
2072
- * }
2073
- * };
2074
- *
2075
- * return <button onClick={handleSubscribe}>Subscribe</button>;
2076
- * }
2077
- * ```
2078
- */
2079
- declare const useCreateCheckoutSession: (workspaceId: string | null | undefined) => {
2080
- createCheckoutSession: (request: ICheckoutSessionRequest) => Promise<CheckoutResult>;
2081
- loading: boolean;
2082
- error: string | null;
2083
- };
2084
- /**
2085
- * Hook to update subscription (upgrade/downgrade).
2086
- * Returns checkout session if payment is required, otherwise returns subscription update response.
2087
- *
2088
- * @param workspaceId - The workspace ID to update subscription for. Can be null/undefined.
2089
- * @returns An object containing:
2090
- * - `updateSubscription(planVersionId, options?)`: Function to update subscription (throws if workspaceId is null)
2091
- * - `loading`: Boolean indicating if subscription is being updated
2092
- * - `error`: Error message string (null if no error)
2093
- *
2094
- * @example
2095
- * ```tsx
2096
- * function UpgradeButton({ planVersionId }) {
2097
- * const { currentWorkspace } = useSaaSWorkspaces();
2098
- * const { updateSubscription, loading } = useUpdateSubscription(currentWorkspace?._id);
2099
- *
2100
- * const handleUpgrade = async () => {
2101
- * try {
2102
- * const result = await updateSubscription(planVersionId, {
2103
- * billingInterval: 'monthly',
2104
- * successUrl: window.location.href,
2105
- * cancelUrl: window.location.href,
2106
- * });
2107
- *
2108
- * // Check if payment is required
2109
- * if ('checkoutUrl' in result) {
2110
- * window.location.href = result.checkoutUrl;
2111
- * } else {
2112
- * // Subscription updated without payment
2113
- * alert('Subscription updated successfully!');
2114
- * }
2115
- * } catch (error) {
2116
- * console.error('Failed to update subscription:', error);
2117
- * }
2118
- * };
2119
- *
2120
- * return (
2121
- * <button onClick={handleUpgrade} disabled={loading}>
2122
- * {loading ? 'Upgrading...' : 'Upgrade'}
2123
- * </button>
2124
- * );
2125
- * }
2126
- * ```
2127
- *
2128
- * @example
2129
- * ```tsx
2130
- * // Handle both checkout and direct update responses
2131
- * function SubscriptionUpdater({ planVersionId }) {
2132
- * const { updateSubscription } = useUpdateSubscription(workspaceId);
2133
- *
2134
- * const handleUpdate = async () => {
2135
- * const result = await updateSubscription(planVersionId);
2136
- *
2137
- * // Type guard to check response type
2138
- * if ('checkoutUrl' in result) {
2139
- * // Redirect to payment
2140
- * window.location.href = result.checkoutUrl;
2141
- * } else {
2142
- * // Direct update successful
2143
- * console.log('Updated subscription:', result.subscription);
2144
- * }
2145
- * };
2146
- *
2147
- * return <button onClick={handleUpdate}>Update</button>;
2148
- * }
2149
- * ```
2150
- */
2151
- declare const useUpdateSubscription: (workspaceId: string | null | undefined) => {
2152
- updateSubscription: (planVersionId: string, options?: {
2153
- billingInterval?: BillingInterval;
2154
- successUrl?: string;
2155
- cancelUrl?: string;
2156
- }) => Promise<ISubscriptionUpdateResponse | ICheckoutSessionResponse>;
2157
- loading: boolean;
2158
- error: string | null;
2159
- };
2160
- /**
2161
- * Combined hook that provides both subscription and plan group data.
2162
- * Useful for subscription management pages that need both pieces of data.
2163
- * Combines useSubscription, usePlanGroup, and useUpdateSubscription.
2164
- *
2165
- * @param workspaceId - The workspace ID. Can be null/undefined to disable fetching.
2166
- * @param groupVersionId - Optional: specific group version ID to fetch
2167
- * @returns An object containing:
2168
- * - `subscription`: Current subscription data (from useSubscription)
2169
- * - `planGroup`: Plan group data (from usePlanGroup)
2170
- * - `loading`: Boolean indicating if any operation is in progress
2171
- * - `error`: Error message string (null if no error)
2172
- * - `updateSubscription(planVersionId, options?)`: Function to update subscription
2173
- * - `refetch()`: Function to refetch both subscription and plan group
2174
- *
2175
- * @example
2176
- * ```tsx
2177
- * function SubscriptionManagementPage() {
2178
- * const { currentWorkspace } = useSaaSWorkspaces();
2179
- * const {
2180
- * subscription,
2181
- * planGroup,
2182
- * loading,
2183
- * updateSubscription,
2184
- * refetch,
2185
- * } = useSubscriptionManagement(currentWorkspace?._id);
2186
- *
2187
- * if (loading) return <Loading />;
2188
- *
2189
- * return (
2190
- * <div>
2191
- * <CurrentPlan subscription={subscription} />
2192
- * <AvailablePlans planGroup={planGroup} onSelect={updateSubscription} />
2193
- * <button onClick={refetch}>Refresh</button>
2194
- * </div>
2195
- * );
2196
- * }
2197
- * ```
2198
- */
2199
- declare const useSubscriptionManagement: (workspaceId: string | null | undefined, groupVersionId?: string | null) => {
2200
- subscription: ISubscriptionResponse | null;
2201
- planGroup: IPlanGroupResponse | null;
2202
- loading: boolean;
2203
- error: string | null;
2204
- updateSubscription: (planVersionId: string, options?: {
2205
- billingInterval?: BillingInterval;
2206
- successUrl?: string;
2207
- cancelUrl?: string;
2208
- }) => Promise<ISubscriptionUpdateResponse | ICheckoutSessionResponse>;
2209
- refetch: () => Promise<void>;
2210
- };
2211
- /**
2212
- * Hook to list invoices for a workspace subscription with pagination support.
2213
- * Automatically fetches when workspaceId, limit, or startingAfter changes.
2214
- *
2215
- * @param workspaceId - The workspace ID to get invoices for. Can be null/undefined to disable fetching.
2216
- * @param limit - Number of invoices to return (default: 10)
2217
- * @param startingAfter - Invoice ID to start after (for pagination)
2218
- * @returns An object containing:
2219
- * - `invoices`: Array of invoice objects
2220
- * - `hasMore`: Boolean indicating if there are more invoices to load
2221
- * - `loading`: Boolean indicating if invoices are being fetched
2222
- * - `error`: Error message string (null if no error)
2223
- * - `refetch()`: Function to manually refetch invoices
2224
- *
2225
- * @example
2226
- * ```tsx
2227
- * function InvoiceList() {
2228
- * const { currentWorkspace } = useSaaSWorkspaces();
2229
- * const { invoices, hasMore, loading, refetch } = useInvoices(currentWorkspace?._id, 10);
2230
- *
2231
- * if (loading) return <Loading />;
2232
- *
2233
- * return (
2234
- * <div>
2235
- * {invoices.map(invoice => (
2236
- * <InvoiceCard key={invoice._id} invoice={invoice} />
2237
- * ))}
2238
- * {hasMore && <button onClick={() => refetch()}>Load More</button>}
2239
- * </div>
2240
- * );
2241
- * }
2242
- * ```
2243
- *
2244
- * @example
2245
- * ```tsx
2246
- * // Pagination example
2247
- * function PaginatedInvoices() {
2248
- * const [lastInvoiceId, setLastInvoiceId] = useState<string | undefined>();
2249
- * const { currentWorkspace } = useSaaSWorkspaces();
2250
- * const { invoices, hasMore, refetch } = useInvoices(
2251
- * currentWorkspace?._id,
2252
- * 10,
2253
- * lastInvoiceId
2254
- * );
2255
- *
2256
- * const loadMore = () => {
2257
- * if (invoices.length > 0) {
2258
- * setLastInvoiceId(invoices[invoices.length - 1]._id);
2259
- * }
2260
- * };
2261
- *
2262
- * return (
2263
- * <div>
2264
- * {invoices.map(invoice => <InvoiceCard key={invoice._id} invoice={invoice} />)}
2265
- * {hasMore && <button onClick={loadMore}>Load More</button>}
2266
- * </div>
2267
- * );
2268
- * }
2269
- * ```
2270
- */
2271
- declare const useInvoices: (workspaceId: string | null | undefined, limit?: number, startingAfter?: string) => {
2272
- invoices: IInvoice[];
2273
- hasMore: boolean;
2274
- loading: boolean;
2275
- error: string | null;
2276
- refetch: () => Promise<void>;
2277
- };
2278
- /**
2279
- * Hook to open the Stripe Customer Portal for managing payment methods, invoices, and subscription.
2280
- *
2281
- * @example
2282
- * ```tsx
2283
- * const { openBillingPortal, loading } = useBillingPortal(workspaceId);
2284
- * <Button onClick={() => openBillingPortal()} disabled={loading}>
2285
- * Manage Payment Method
2286
- * </Button>
2287
- * ```
2288
- */
2289
- declare const useBillingPortal: (workspaceId: string | null | undefined) => {
2290
- openBillingPortal: (returnUrl?: string) => Promise<{
2291
- url: string;
2292
- }>;
2293
- loading: boolean;
2294
- error: string | null;
2295
- };
2296
- /**
2297
- * Hook to get a single invoice by ID.
2298
- * Automatically fetches when workspaceId or invoiceId changes.
2299
- *
2300
- * @param workspaceId - The workspace ID. Can be null/undefined to disable fetching.
2301
- * @param invoiceId - The invoice ID to fetch. Can be null/undefined to disable fetching.
2302
- * @returns An object containing:
2303
- * - `invoice`: Invoice data object (null if not loaded)
2304
- * - `loading`: Boolean indicating if invoice is being fetched
2305
- * - `error`: Error message string (null if no error)
2306
- * - `refetch()`: Function to manually refetch the invoice
2307
- *
2308
- * @example
2309
- * ```tsx
2310
- * function InvoiceDetails({ invoiceId }) {
2311
- * const { currentWorkspace } = useSaaSWorkspaces();
2312
- * const { invoice, loading, error } = useInvoice(currentWorkspace?._id, invoiceId);
2313
- *
2314
- * if (loading) return <Loading />;
2315
- * if (error) return <Error message={error} />;
2316
- * if (!invoice) return <p>Invoice not found</p>;
2317
- *
2318
- * return (
2319
- * <div>
2320
- * <h2>Invoice #{invoice.invoiceNumber}</h2>
2321
- * <p>Amount: ${invoice.amount}</p>
2322
- * <p>Status: {invoice.status}</p>
2323
- * </div>
2324
- * );
2325
- * }
2326
- * ```
2327
- */
2328
- declare const useInvoice: (workspaceId: string | null | undefined, invoiceId: string | null | undefined) => {
2329
- invoice: IInvoice | null;
2330
- loading: boolean;
2331
- error: string | null;
2332
- refetch: () => Promise<void>;
2333
- };
2334
- /**
2335
- * Hook to cancel a subscription at the end of the current billing period.
2336
- * Sets cancelAtPeriodEnd: true - subscription remains active until period ends.
2337
- *
2338
- * @param workspaceId - The workspace ID. Can be null/undefined.
2339
- * @returns An object containing:
2340
- * - `cancelSubscription()`: Function to cancel subscription at period end
2341
- * - `loading`: Boolean indicating if cancellation is in progress
2342
- * - `error`: Error message string (null if no error)
2343
- *
2344
- * @example
2345
- * ```tsx
2346
- * function CancelSubscriptionButton() {
2347
- * const { currentWorkspace } = useSaaSWorkspaces();
2348
- * const { cancelSubscription, loading } = useCancelSubscription(currentWorkspace?._id);
2349
- * const { refetch } = useSubscription(currentWorkspace?._id);
2350
- *
2351
- * const handleCancel = async () => {
2352
- * try {
2353
- * await cancelSubscription();
2354
- * await refetch(); // Refresh subscription data
2355
- * alert('Subscription will be canceled at the end of the billing period');
2356
- * } catch (error) {
2357
- * console.error('Failed to cancel:', error);
2358
- * }
2359
- * };
2360
- *
2361
- * return (
2362
- * <button onClick={handleCancel} disabled={loading}>
2363
- * {loading ? 'Canceling...' : 'Cancel Subscription'}
2364
- * </button>
2365
- * );
2366
- * }
2367
- * ```
2368
- */
2369
- declare const useCancelSubscription: (workspaceId: string | null | undefined) => {
2370
- cancelSubscription: () => Promise<ISubscriptionResponse>;
2371
- loading: boolean;
2372
- error: string | null;
2373
- };
2374
- /**
2375
- * Hook to resume a subscription that was scheduled for cancellation.
2376
- * Sets cancelAtPeriodEnd: false - subscription will continue after period ends.
2377
- *
2378
- * @param workspaceId - The workspace ID. Can be null/undefined.
2379
- * @returns An object containing:
2380
- * - `resumeSubscription()`: Function to resume subscription
2381
- * - `loading`: Boolean indicating if resume is in progress
2382
- * - `error`: Error message string (null if no error)
2383
- *
2384
- * @example
2385
- * ```tsx
2386
- * function ResumeSubscriptionButton() {
2387
- * const { currentWorkspace } = useSaaSWorkspaces();
2388
- * const { resumeSubscription, loading } = useResumeSubscription(currentWorkspace?._id);
2389
- * const { refetch } = useSubscription(currentWorkspace?._id);
2390
- *
2391
- * const handleResume = async () => {
2392
- * try {
2393
- * await resumeSubscription();
2394
- * await refetch(); // Refresh subscription data
2395
- * alert('Subscription has been resumed');
2396
- * } catch (error) {
2397
- * console.error('Failed to resume:', error);
2398
- * }
2399
- * };
2400
- *
2401
- * return (
2402
- * <button onClick={handleResume} disabled={loading}>
2403
- * {loading ? 'Resuming...' : 'Resume Subscription'}
2404
- * </button>
2405
- * );
2406
- * }
2407
- * ```
2408
- */
2409
- declare const useResumeSubscription: (workspaceId: string | null | undefined) => {
2410
- resumeSubscription: () => Promise<ISubscriptionResponse>;
2411
- loading: boolean;
2412
- error: string | null;
2413
- };
2414
- /**
2415
- * Hook to record quota usage for a workspace.
2416
- * Returns a function to record usage (mutation pattern).
2417
- *
2418
- * @param workspaceId - The workspace ID. Can be null/undefined.
2419
- * @returns An object containing:
2420
- * - `recordUsage(request)`: Function to record usage (throws if workspaceId is null)
2421
- * - `loading`: Boolean indicating if recording is in progress
2422
- * - `error`: Error message string (null if no error)
2423
- *
2424
- * @example
2425
- * ```tsx
2426
- * function RecordUsageButton() {
2427
- * const { currentWorkspace } = useSaaSWorkspaces();
2428
- * const { recordUsage, loading } = useRecordUsage(currentWorkspace?._id);
2429
- *
2430
- * const handleRecord = async () => {
2431
- * try {
2432
- * const result = await recordUsage({
2433
- * quotaSlug: 'api_calls',
2434
- * quantity: 1,
2435
- * source: 'web-app',
2436
- * });
2437
- * console.log(`Used: ${result.consumed}/${result.included}`);
2438
- * } catch (error) {
2439
- * console.error('Failed to record usage:', error);
2440
- * }
2441
- * };
2442
- *
2443
- * return (
2444
- * <button onClick={handleRecord} disabled={loading}>
2445
- * {loading ? 'Recording...' : 'Record Usage'}
2446
- * </button>
2447
- * );
2448
- * }
2449
- * ```
2450
- */
2451
- declare const useRecordUsage: (workspaceId: string | null | undefined) => {
2452
- recordUsage: (request: IRecordUsageRequest) => Promise<IRecordUsageResponse>;
2453
- loading: boolean;
2454
- error: string | null;
2455
- };
2456
- /**
2457
- * Hook to get usage status for a single quota.
2458
- * Automatically fetches when workspaceId or quotaSlug changes.
2459
- *
2460
- * @param workspaceId - The workspace ID. Can be null/undefined to disable fetching.
2461
- * @param quotaSlug - The quota slug to check. Can be null/undefined to disable fetching.
2462
- * @returns An object containing:
2463
- * - `status`: Quota usage status (null if not loaded)
2464
- * - `loading`: Boolean indicating if status is being fetched
2465
- * - `error`: Error message string (null if no error)
2466
- * - `refetch()`: Function to manually refetch the status
2467
- *
2468
- * @example
2469
- * ```tsx
2470
- * function QuotaStatusDisplay() {
2471
- * const { currentWorkspace } = useSaaSWorkspaces();
2472
- * const { status, loading } = useQuotaUsageStatus(currentWorkspace?._id, 'api_calls');
2473
- *
2474
- * if (loading) return <Loading />;
2475
- * if (!status) return null;
2476
- *
2477
- * return (
2478
- * <div>
2479
- * <p>Used: {status.consumed} / {status.included}</p>
2480
- * <p>Available: {status.available}</p>
2481
- * {status.hasOverage && <p>Overage: {status.overage}</p>}
2482
- * </div>
2483
- * );
2484
- * }
2485
- * ```
2486
- */
2487
- declare const useQuotaUsageStatus: (workspaceId: string | null | undefined, quotaSlug: string | null | undefined) => {
2488
- status: IQuotaUsageStatusResponse | null;
2489
- loading: boolean;
2490
- error: string | null;
2491
- refetch: () => Promise<void>;
2492
- };
2493
- /**
2494
- * Hook to get usage status for all quotas in the workspace's current plan.
2495
- * Automatically fetches when workspaceId changes.
2496
- *
2497
- * @param workspaceId - The workspace ID. Can be null/undefined to disable fetching.
2498
- * @returns An object containing:
2499
- * - `quotas`: Record of quota usage statuses keyed by slug (null if not loaded)
2500
- * - `loading`: Boolean indicating if statuses are being fetched
2501
- * - `error`: Error message string (null if no error)
2502
- * - `refetch()`: Function to manually refetch all statuses
2503
- *
2504
- * @example
2505
- * ```tsx
2506
- * function AllQuotasDisplay() {
2507
- * const { currentWorkspace } = useSaaSWorkspaces();
2508
- * const { quotas, loading } = useAllQuotaUsage(currentWorkspace?._id);
2509
- *
2510
- * if (loading) return <Loading />;
2511
- * if (!quotas) return null;
2512
- *
2513
- * return (
2514
- * <div>
2515
- * {Object.entries(quotas).map(([slug, usage]) => (
2516
- * <div key={slug}>
2517
- * <p>{slug}: {usage.consumed}/{usage.included}</p>
2518
- * {usage.hasOverage && <p>Overage: {usage.overage}</p>}
2519
- * </div>
2520
- * ))}
2521
- * </div>
2522
- * );
2523
- * }
2524
- * ```
2525
- */
2526
- declare const useAllQuotaUsage: (workspaceId: string | null | undefined) => {
2527
- quotas: Record<string, IQuotaUsageStatus> | null;
2528
- loading: boolean;
2529
- error: string | null;
2530
- refetch: () => Promise<void>;
2531
- };
2532
- /**
2533
- * Hook to get paginated usage logs for a workspace.
2534
- * Automatically fetches when workspaceId or filter params change.
2535
- *
2536
- * @param workspaceId - The workspace ID. Can be null/undefined to disable fetching.
2537
- * @param quotaSlug - Optional quota slug to filter logs by.
2538
- * @param options - Optional filters: from, to, source, page, limit
2539
- * @returns An object containing:
2540
- * - `logs`: Array of usage log entries
2541
- * - `totalDocs`: Total number of log entries matching the query
2542
- * - `totalPages`: Total number of pages
2543
- * - `page`: Current page number
2544
- * - `hasNextPage`: Whether there are more pages after the current one
2545
- * - `hasPrevPage`: Whether there are pages before the current one
2546
- * - `loading`: Boolean indicating if logs are being fetched
2547
- * - `error`: Error message string (null if no error)
2548
- * - `refetch()`: Function to manually refetch logs
2549
- *
2550
- * @example
2551
- * ```tsx
2552
- * function UsageLogsTable() {
2553
- * const { currentWorkspace } = useSaaSWorkspaces();
2554
- * const { logs, totalPages, page, hasNextPage, loading } = useUsageLogs(
2555
- * currentWorkspace?._id,
2556
- * 'api_calls',
2557
- * { limit: 20 }
2558
- * );
2559
- *
2560
- * if (loading) return <Loading />;
2561
- *
2562
- * return (
2563
- * <div>
2564
- * {logs.map(log => (
2565
- * <div key={log._id}>
2566
- * {log.quotaSlug}: {log.quantity} ({log.createdAt})
2567
- * </div>
2568
- * ))}
2569
- * <p>Page {page} of {totalPages}</p>
2570
- * </div>
2571
- * );
2572
- * }
2573
- * ```
2574
- */
2575
- declare const useUsageLogs: (workspaceId: string | null | undefined, quotaSlug?: string, options?: {
2576
- from?: string;
2577
- to?: string;
2578
- source?: string;
2579
- page?: number;
2580
- limit?: number;
2581
- }) => {
2582
- logs: IUsageLogEntry[];
2583
- totalDocs: number;
2584
- totalPages: number;
2585
- page: number;
2586
- hasNextPage: boolean;
2587
- hasPrevPage: boolean;
2588
- loading: boolean;
2589
- error: string | null;
2590
- refetch: () => Promise<void>;
2591
- };
2592
-
2593
- interface TrialStatus {
2594
- /** Whether the current subscription is in trial. */
2595
- isTrialing: boolean;
2596
- /** Number of days remaining in the trial. 0 if not trialing or expired. */
2597
- daysRemaining: number;
2598
- /** Trial end date as a Date object. null if not trialing. */
2599
- trialEndsAt: Date | null;
2600
- /** Trial start date as a Date object. null if not trialing. */
2601
- trialStartedAt: Date | null;
2602
- /** Whether the trial is ending soon (<= 3 days remaining). */
2603
- isTrialEnding: boolean;
2604
- }
2605
- /**
2606
- * Hook that computes trial status from the current subscription context.
2607
- * Must be used within SubscriptionContextProvider.
2608
- *
2609
- * @returns TrialStatus — computed trial information
2610
- *
2611
- * @example
2612
- * ```tsx
2613
- * const { isTrialing, daysRemaining, isTrialEnding } = useTrialStatus();
2614
- *
2615
- * if (isTrialEnding) {
2616
- * return <UpgradeBanner daysLeft={daysRemaining} />;
2617
- * }
2618
- * ```
2619
- */
2620
- declare function useTrialStatus(): TrialStatus;
2621
-
2622
- interface SeatStatus {
2623
- /** Whether the current plan uses seat-based pricing. */
2624
- hasSeatPricing: boolean;
2625
- /** Total workspace members. */
2626
- memberCount: number;
2627
- /** Seats included in the base price (free). */
2628
- includedSeats: number;
2629
- /** Maximum seats allowed. 0 = unlimited. */
2630
- maxSeats: number;
2631
- /** Seats beyond included that are being billed. */
2632
- billableSeats: number;
2633
- /** Remaining seats before hitting max. Infinity if unlimited. */
2634
- availableSeats: number;
2635
- /** Whether workspace is at max seat capacity. */
2636
- isAtMax: boolean;
2637
- /** Whether workspace is near max (>= 80% used). */
2638
- isNearMax: boolean;
2639
- /** Per-seat price in cents for the current billing interval. Null if not applicable. */
2640
- perSeatPriceCents: number | null;
2641
- /** Billing currency. */
2642
- currency: string;
1016
+ createBillingPortalSession(workspaceId: string, returnUrl?: string): Promise<{
1017
+ url: string;
1018
+ }>;
1019
+ /**
1020
+ * List invoices for a workspace subscription
1021
+ * @param workspaceId - The workspace ID
1022
+ * @param limit - Number of invoices to return (default: 10)
1023
+ * @param startingAfter - Invoice ID to start after (for pagination)
1024
+ * @returns List of invoices with pagination info
1025
+ */
1026
+ listInvoices(workspaceId: string, limit?: number, startingAfter?: string): Promise<IInvoiceListResponse>;
1027
+ /**
1028
+ * Get a single invoice by ID
1029
+ * @param workspaceId - The workspace ID
1030
+ * @param invoiceId - The invoice ID
1031
+ * @returns Invoice details
1032
+ */
1033
+ getInvoice(workspaceId: string, invoiceId: string): Promise<IInvoiceResponse>;
1034
+ /**
1035
+ * Cancel subscription at the end of the current billing period
1036
+ * Sets cancelAtPeriodEnd: true - subscription remains active until period ends
1037
+ * @param workspaceId - The workspace ID
1038
+ * @returns Updated subscription with cancelAtPeriodEnd and stripeCurrentPeriodEnd
1039
+ */
1040
+ cancelSubscriptionAtPeriodEnd(workspaceId: string): Promise<ISubscriptionResponse>;
1041
+ /**
1042
+ * Resume a subscription that was scheduled for cancellation
1043
+ * Sets cancelAtPeriodEnd: false - subscription will continue after period ends
1044
+ * @param workspaceId - The workspace ID
1045
+ * @returns Updated subscription with cancelAtPeriodEnd set to false
1046
+ */
1047
+ resumeSubscription(workspaceId: string): Promise<ISubscriptionResponse>;
1048
+ /**
1049
+ * Record quota usage for a workspace
1050
+ * @param workspaceId - The workspace ID
1051
+ * @param request - Usage request with quotaSlug, quantity, and optional metadata/source
1052
+ * @returns Usage result with consumed/included/available/overage
1053
+ */
1054
+ recordUsage(workspaceId: string, request: IRecordUsageRequest): Promise<IRecordUsageResponse>;
1055
+ /**
1056
+ * Get usage status for a single quota
1057
+ * @param workspaceId - The workspace ID
1058
+ * @param quotaSlug - The quota slug to check
1059
+ * @returns Quota usage status with consumed/included/available/overage/hasOverage
1060
+ */
1061
+ getQuotaUsageStatus(workspaceId: string, quotaSlug: string): Promise<IQuotaUsageStatusResponse>;
1062
+ /**
1063
+ * Get usage status for all quotas in the workspace's current plan
1064
+ * @param workspaceId - The workspace ID
1065
+ * @returns All quota usage statuses keyed by quota slug
1066
+ */
1067
+ getAllQuotaUsage(workspaceId: string): Promise<IAllQuotaUsageResponse>;
1068
+ /**
1069
+ * Get paginated usage logs for a workspace
1070
+ * @param workspaceId - The workspace ID
1071
+ * @param query - Optional filters: quotaSlug, from, to, source, page, limit
1072
+ * @returns Paginated usage log entries
1073
+ */
1074
+ getUsageLogs(workspaceId: string, query?: IUsageLogsQuery): Promise<IUsageLogsResponse>;
2643
1075
  }
2644
- /**
2645
- * Hook that computes seat status from subscription context and workspace data.
2646
- * Must be used within SubscriptionContextProvider.
2647
- *
2648
- * @param workspace - The current workspace (needs users array)
2649
- * @returns SeatStatus — computed seat information
2650
- *
2651
- * @example
2652
- * ```tsx
2653
- * const { isAtMax, availableSeats, billableSeats } = useSeatStatus(workspace);
2654
- *
2655
- * if (isAtMax) {
2656
- * return <UpgradeBanner />;
2657
- * }
2658
- * ```
2659
- */
2660
- declare function useSeatStatus(workspace: {
2661
- users?: any[];
2662
- billingCurrency?: string | null;
2663
- } | null): SeatStatus;
2664
-
2665
- interface PushNotificationState {
2666
- /** Browser's Notification.permission: 'default' | 'granted' | 'denied' */
2667
- permission: NotificationPermission;
2668
- /** Whether this device is subscribed to push notifications */
2669
- isSubscribed: boolean;
2670
- /** Whether the browser supports push notifications */
2671
- isSupported: boolean;
2672
- /** Loading state for subscribe/unsubscribe operations */
2673
- loading: boolean;
2674
- /** Error message from the last operation */
2675
- error: string | null;
2676
- }
2677
- interface PushNotificationContextValue extends PushNotificationState {
2678
- /** Request notification permission from the browser. Returns true if granted. */
2679
- requestPermission: () => Promise<boolean>;
2680
- /** Subscribe this device to push notifications (requests permission if needed). */
2681
- subscribe: () => Promise<void>;
2682
- /** Unsubscribe this device from push notifications. */
2683
- unsubscribe: () => Promise<void>;
2684
- }
2685
- interface PushNotificationProviderProps {
2686
- children: react__default.ReactNode;
2687
- /** Path to the service worker file (default: '/push-sw.js') */
2688
- serviceWorkerPath?: string;
2689
- /** Automatically subscribe after permission is granted on login (default: false) */
2690
- autoSubscribe?: boolean;
2691
- }
2692
- declare const PushNotificationProvider: react__default.FC<PushNotificationProviderProps>;
2693
- /**
2694
- * Hook to access push notification state and actions.
2695
- * Must be used within PushNotificationProvider (included in SaaSOSProvider when push config is provided).
2696
- */
2697
- declare function usePushNotifications(): PushNotificationContextValue;
2698
-
2699
- /**
2700
- * Push notification service worker source code.
2701
- * Export this string and write it to a file in your app's `public/` directory.
2702
- *
2703
- * @example
2704
- * ```ts
2705
- * // scripts/generate-sw.ts
2706
- * import { PUSH_SERVICE_WORKER_SCRIPT } from '@buildbase/sdk';
2707
- * import fs from 'fs';
2708
- * fs.writeFileSync('public/push-sw.js', PUSH_SERVICE_WORKER_SCRIPT);
2709
- * ```
2710
- *
2711
- * Or manually create `public/push-sw.js` with this content.
2712
- */
2713
- 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";
2714
1076
 
2715
1077
  /**
2716
1078
  * EventEmitter class to handle and trigger event callbacks
@@ -2792,14 +1154,16 @@ declare class EventEmitter {
2792
1154
  declare const eventEmitter: EventEmitter;
2793
1155
 
2794
1156
  /**
2795
- * Centralized API client for organization (OS) settings.
2796
- * Extends BaseApi for shared URL/auth/request handling.
1157
+ * Notify all subscribers to refetch subscription (e.g. after update/cancel/resume).
1158
+ * Called internally by useUpdateSubscription, useCancelSubscription, useResumeSubscription on success.
2797
1159
  */
1160
+ declare function invalidateSubscription(): void;
2798
1161
 
2799
- declare class SettingsApi extends BaseApi {
2800
- constructor(config: IOsConfig);
2801
- getSettings(signal?: AbortSignal): Promise<ISettings>;
2802
- }
1162
+ /**
1163
+ * Notify all subscribers to refetch quota usage (e.g. after recording usage).
1164
+ * Called internally by useRecordUsage on success.
1165
+ */
1166
+ declare function invalidateQuotaUsage(): void;
2803
1167
 
2804
1168
  /**
2805
1169
  * Centralized formatting for subscription/quota display: cents, overage rates, included + overage text.
@@ -2846,6 +1210,30 @@ declare function getQuotaUnitLabelFromName(nameOrSlug: string): string;
2846
1210
  */
2847
1211
  declare function formatQuotaIncludedOverage(included: number | undefined, overageCents: number | undefined, unitLabel: string, currency: string, unitSize?: number): string;
2848
1212
 
1213
+ type QuotaDisplayValue = {
1214
+ included: number;
1215
+ overage?: number;
1216
+ unitSize?: number;
1217
+ } | null;
1218
+ /**
1219
+ * Normalize a per-interval quota value to a display shape for the given billing interval.
1220
+ */
1221
+ declare function getQuotaDisplayValue(value: IQuotaByInterval | null | undefined, interval?: BillingInterval): QuotaDisplayValue;
1222
+ /** Options for formatting quota with price. */
1223
+ interface FormatQuotaWithPriceOptions {
1224
+ /** If true, overage is in cents (Stripe); format as dollars. Default true. */
1225
+ overageInCents?: boolean;
1226
+ /** Currency symbol override. When omitted, derived from `currency` (empty if no currency). */
1227
+ currencySymbol?: string;
1228
+ /** Stripe currency code (e.g. 'usd', 'inr'). Used to resolve symbol when currencySymbol is not set. */
1229
+ currency?: string;
1230
+ }
1231
+ /**
1232
+ * Format a quota display value as "X included, then $Y.YY / unit" (e.g. "10 included, then $10.00 / video").
1233
+ * Assumes overage is the per-unit price (in cents when overageInCents is true).
1234
+ */
1235
+ declare function formatQuotaWithPrice(value: QuotaDisplayValue, unitName: string, options?: FormatQuotaWithPriceOptions): string;
1236
+
2849
1237
  /**
2850
1238
  * Helpers for multi-currency plan version pricing (pricingVariants).
2851
1239
  */
@@ -2912,30 +1300,361 @@ declare function calculateSeatOverageCents(planVersion: IPlanVersion, currency:
2912
1300
  * Does not include metered usage (billed at period end).
2913
1301
  */
2914
1302
  declare function calculateTotalSubscriptionCents(planVersion: IPlanVersion, currency: string, interval: BillingInterval, currentSeatCount?: number): number | null;
2915
-
2916
- type QuotaDisplayValue = {
2917
- included: number;
2918
- overage?: number;
2919
- unitSize?: number;
2920
- } | null;
1303
+ interface MaxUsersConfig {
1304
+ /** The resolved maximum number of users allowed. 0 = unlimited. */
1305
+ maxUsers: number;
1306
+ /** Where the limit comes from. */
1307
+ source: 'seat_pricing' | 'settings' | 'none';
1308
+ /** Seats included in the base plan price (0 if no seat pricing). */
1309
+ includedSeats: number;
1310
+ /** Whether seat-based pricing is active. */
1311
+ hasSeatPricing: boolean;
1312
+ }
2921
1313
  /**
2922
- * Normalize a per-interval quota value to a display shape for the given billing interval.
1314
+ * Resolve the effective max users limit from the plan's seat pricing config,
1315
+ * falling back to settings when no seat pricing is configured.
1316
+ *
1317
+ * Priority:
1318
+ * 1. Seat pricing `maxSeats` (from plan version's pricing variant) — the plan controls the limit
1319
+ * 2. Settings fallback (`workspace.maxWorkspaceUsers`) — for workspaces without a subscription
1320
+ *
1321
+ * Returns 0 if no limit is set (unlimited).
2923
1322
  */
2924
- declare function getQuotaDisplayValue(value: IQuotaByInterval | null | undefined, interval?: BillingInterval): QuotaDisplayValue;
2925
- /** Options for formatting quota with price. */
2926
- interface FormatQuotaWithPriceOptions {
2927
- /** If true, overage is in cents (Stripe); format as dollars. Default true. */
2928
- overageInCents?: boolean;
2929
- /** Currency symbol override. When omitted, derived from `currency` (empty if no currency). */
2930
- currencySymbol?: string;
2931
- /** Stripe currency code (e.g. 'usd', 'inr'). Used to resolve symbol when currencySymbol is not set. */
1323
+ declare function resolveMaxUsers(opts: {
1324
+ planVersion?: IPlanVersion | null;
2932
1325
  currency?: string;
1326
+ settingsMaxUsers?: number | null;
1327
+ }): MaxUsersConfig;
1328
+ type InviteBlockReason = 'seat_limit_reached' | 'settings_user_limit_reached' | 'no_subscription' | null;
1329
+ interface InviteValidation {
1330
+ /** Whether a new user can be invited. */
1331
+ canInvite: boolean;
1332
+ /** Reason the invite is blocked, or null if allowed. */
1333
+ blockReason: InviteBlockReason;
1334
+ /** Human-readable message for the block reason. */
1335
+ blockMessage: string | null;
2933
1336
  }
2934
1337
  /**
2935
- * Format a quota display value as "X included, then $Y.YY / unit" (e.g. "10 included, then $10.00 / video").
2936
- * Assumes overage is the per-unit price (in cents when overageInCents is true).
1338
+ * Validate whether a new member can be invited given current seat/user limits.
2937
1339
  */
2938
- declare function formatQuotaWithPrice(value: QuotaDisplayValue, unitName: string, options?: FormatQuotaWithPriceOptions): string;
1340
+ declare function validateInvite(opts: {
1341
+ memberCount: number;
1342
+ maxUsersConfig: MaxUsersConfig;
1343
+ hasSubscription?: boolean;
1344
+ requireSubscription?: boolean;
1345
+ }): InviteValidation;
1346
+
1347
+ /**
1348
+ * Push notification service worker source code.
1349
+ * Export this string and write it to a file in your app's `public/` directory.
1350
+ *
1351
+ * @example
1352
+ * ```ts
1353
+ * // scripts/generate-sw.ts
1354
+ * import { PUSH_SERVICE_WORKER_SCRIPT } from '@buildbase/sdk';
1355
+ * import fs from 'fs';
1356
+ * fs.writeFileSync('public/push-sw.js', PUSH_SERVICE_WORKER_SCRIPT);
1357
+ * ```
1358
+ *
1359
+ * Or manually create `public/push-sw.js` with this content.
1360
+ */
1361
+ 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";
1362
+
1363
+ declare const countries: {
1364
+ value: string;
1365
+ flag: string;
1366
+ text: string;
1367
+ currencyCode: string;
1368
+ currencyIcon: string;
1369
+ }[];
1370
+
1371
+ declare const uniqueCurrencies: {
1372
+ value: string;
1373
+ text: string;
1374
+ icon: any;
1375
+ }[];
1376
+
1377
+ declare const languages: {
1378
+ value: string;
1379
+ label: string;
1380
+ flag: string;
1381
+ }[];
1382
+
1383
+ declare const timezones: {
1384
+ value: string;
1385
+ abbr: string;
1386
+ offset: number;
1387
+ isdst: boolean;
1388
+ text: string;
1389
+ utc: string[];
1390
+ }[];
1391
+
1392
+ declare const formSchema: z.ZodObject<{
1393
+ name: z.ZodString;
1394
+ email: z.ZodString;
1395
+ }, z.core.$strip>;
1396
+ type formValuesType = z.infer<typeof formSchema>;
1397
+
1398
+ interface BetaFormData {
1399
+ name?: string;
1400
+ email: string;
1401
+ }
1402
+ interface BetaFormResponse {
1403
+ success: boolean;
1404
+ message: string;
1405
+ }
1406
+
1407
+ /**
1408
+ * Server-side BuildBase SDK — Auth.js-style configuration pattern.
1409
+ *
1410
+ * Configure once, use everywhere. Session is resolved automatically.
1411
+ *
1412
+ * @example 1. Configure once (lib/buildbase.ts)
1413
+ * ```ts
1414
+ * import BuildBase from "@buildbase/sdk"
1415
+ * import { cookies } from "next/headers"
1416
+ *
1417
+ * export const { auth, workspace, subscription, usage } = BuildBase({
1418
+ * serverUrl: process.env.BUILDBASE_URL!,
1419
+ * orgId: process.env.BUILDBASE_ORG_ID!,
1420
+ * getSessionId: async () => {
1421
+ * const c = await cookies()
1422
+ * return c.get("bb-session-id")?.value ?? null
1423
+ * },
1424
+ * })
1425
+ * ```
1426
+ *
1427
+ * @example 2. Use in API routes — no sessionId passing needed
1428
+ * ```ts
1429
+ * import { auth, workspace, subscription } from "@/lib/buildbase"
1430
+ *
1431
+ * export async function GET() {
1432
+ * const session = await auth()
1433
+ * if (!session) return Response.json({ error: "Unauthorized" }, { status: 401 })
1434
+ *
1435
+ * const workspaces = await workspace.list()
1436
+ * const sub = await subscription.get(workspaceId)
1437
+ * return Response.json({ workspaces, sub })
1438
+ * }
1439
+ * ```
1440
+ *
1441
+ * @example 3. Background jobs — override session for service accounts
1442
+ * ```ts
1443
+ * import { client } from "@/lib/buildbase"
1444
+ *
1445
+ * const api = client.forSession(process.env.SERVICE_SESSION_ID!)
1446
+ * await api.workspace.recordUsage(workspaceId, { quotaSlug: "uploads", quantity: 1 })
1447
+ * ```
1448
+ */
1449
+
1450
+ interface BuildBaseConfig {
1451
+ /** Base URL of the BuildBase API server */
1452
+ serverUrl: string;
1453
+ /** Organization ID */
1454
+ orgId: string;
1455
+ /** API version (default: 'v1') */
1456
+ version?: ApiVersion;
1457
+ /**
1458
+ * Async callback to resolve the current session ID.
1459
+ * Called automatically before each authenticated API call.
1460
+ *
1461
+ * @example Next.js (httpOnly cookie)
1462
+ * ```ts
1463
+ * getSessionId: async () => {
1464
+ * const c = await cookies()
1465
+ * return c.get("bb-session-id")?.value ?? null
1466
+ * }
1467
+ * ```
1468
+ *
1469
+ * @example Express (from request — use withSession() instead)
1470
+ * ```ts
1471
+ * // Don't set getSessionId for Express. Use withSession(req.sessionId) per-request.
1472
+ * ```
1473
+ */
1474
+ getSessionId?: () => Promise<string | null>;
1475
+ /**
1476
+ * Request timeout in milliseconds. (default: 30000 — 30 seconds)
1477
+ * Set to 0 to disable timeout.
1478
+ */
1479
+ timeout?: number;
1480
+ /**
1481
+ * Maximum number of automatic retries for transient network failures.
1482
+ * Uses exponential backoff. (default: 0 — no retries)
1483
+ * Only retries on network errors and 5xx responses, never on 4xx.
1484
+ */
1485
+ maxRetries?: number;
1486
+ /**
1487
+ * Enable debug logging. Logs all API requests/responses to console.
1488
+ * (default: false)
1489
+ */
1490
+ debug?: boolean;
1491
+ /**
1492
+ * Custom headers merged into every request.
1493
+ * Useful for proxies, tracing, or custom auth schemes.
1494
+ *
1495
+ * @example
1496
+ * ```ts
1497
+ * headers: {
1498
+ * 'X-Request-Source': 'backend-cron',
1499
+ * 'X-Trace-Id': traceId,
1500
+ * }
1501
+ * ```
1502
+ */
1503
+ headers?: Record<string, string>;
1504
+ /**
1505
+ * Called when any API request fails. Use for centralized error logging.
1506
+ * The error is still thrown after this callback runs.
1507
+ *
1508
+ * @example
1509
+ * ```ts
1510
+ * onError: (error, context) => {
1511
+ * Sentry.captureException(error, { extra: context })
1512
+ * }
1513
+ * ```
1514
+ */
1515
+ onError?: (error: Error, context: {
1516
+ method: string;
1517
+ path: string;
1518
+ }) => void;
1519
+ /**
1520
+ * Custom fetch implementation. Replaces the global `fetch`.
1521
+ * Useful for testing, proxying, or adding middleware.
1522
+ *
1523
+ * @example Using undici for Node.js
1524
+ * ```ts
1525
+ * import { fetch } from 'undici'
1526
+ * { fetch: fetch as any }
1527
+ * ```
1528
+ */
1529
+ fetch?: typeof globalThis.fetch;
1530
+ }
1531
+ interface BuildBaseSession {
1532
+ sessionId: string;
1533
+ }
1534
+ interface WorkspaceActions {
1535
+ list(): Promise<IWorkspace[]>;
1536
+ get(workspaceId: string): Promise<IWorkspace>;
1537
+ create(data: {
1538
+ name: string;
1539
+ image?: string;
1540
+ }): Promise<IWorkspace>;
1541
+ update(workspaceId: string, data: Partial<IWorkspace>): Promise<IWorkspace>;
1542
+ delete(workspaceId: string): Promise<{
1543
+ success: boolean;
1544
+ }>;
1545
+ }
1546
+ interface UserActions {
1547
+ list(workspaceId: string): Promise<IWorkspaceUser[]>;
1548
+ invite(workspaceId: string, email: string, role: string): Promise<{
1549
+ userId: string;
1550
+ workspace: IWorkspace;
1551
+ message: string;
1552
+ }>;
1553
+ remove(workspaceId: string, userId: string): Promise<{
1554
+ userId: string;
1555
+ workspace: IWorkspace;
1556
+ message: string;
1557
+ }>;
1558
+ updateRole(workspaceId: string, userId: string, role: string): Promise<{
1559
+ userId: string;
1560
+ workspace: IWorkspace;
1561
+ message: string;
1562
+ }>;
1563
+ getProfile(): Promise<IUser>;
1564
+ updateProfile(data: Partial<IUser>): Promise<IUser>;
1565
+ }
1566
+ interface SubscriptionActions {
1567
+ get(workspaceId: string): Promise<ISubscriptionResponse>;
1568
+ checkout(workspaceId: string, request: ICheckoutSessionRequest): Promise<CheckoutResult | ICheckoutSessionResponse>;
1569
+ update(workspaceId: string, request: ISubscriptionUpdateRequest): Promise<ISubscriptionUpdateResponse | ICheckoutSessionResponse>;
1570
+ cancel(workspaceId: string): Promise<ISubscriptionResponse>;
1571
+ resume(workspaceId: string): Promise<ISubscriptionResponse>;
1572
+ getBillingPortalUrl(workspaceId: string, returnUrl?: string): Promise<{
1573
+ url: string;
1574
+ }>;
1575
+ }
1576
+ interface PlanActions {
1577
+ getGroup(workspaceId: string): Promise<IPlanGroupResponse>;
1578
+ getVersions(workspaceId: string): Promise<IPlanGroupVersionsResponse>;
1579
+ /** No auth required */
1580
+ getPublic(slug: string): Promise<IPublicPlansResponse>;
1581
+ /** No auth required */
1582
+ getVersion(groupVersionId: string): Promise<IPlanGroupVersion>;
1583
+ }
1584
+ interface InvoiceActions {
1585
+ list(workspaceId: string, limit?: number, startingAfter?: string): Promise<IInvoiceListResponse>;
1586
+ get(workspaceId: string, invoiceId: string): Promise<IInvoiceResponse>;
1587
+ }
1588
+ interface UsageActions {
1589
+ record(workspaceId: string, request: IRecordUsageRequest): Promise<IRecordUsageResponse>;
1590
+ getQuota(workspaceId: string, quotaSlug: string): Promise<IQuotaUsageStatusResponse>;
1591
+ getAll(workspaceId: string): Promise<IAllQuotaUsageResponse>;
1592
+ getLogs(workspaceId: string, query?: IUsageLogsQuery): Promise<IUsageLogsResponse>;
1593
+ }
1594
+ interface SettingsActions {
1595
+ get(): Promise<ISettings>;
1596
+ }
1597
+ interface FeatureActions {
1598
+ list(): Promise<IWorkspaceFeature[]>;
1599
+ update(workspaceId: string, key: string, value: boolean): Promise<IWorkspace>;
1600
+ }
1601
+ /** All action modules bound to a specific session. Returned by `withSession()`. */
1602
+ interface ScopedActions {
1603
+ workspace: WorkspaceActions;
1604
+ users: UserActions;
1605
+ subscription: SubscriptionActions;
1606
+ plans: PlanActions;
1607
+ invoices: InvoiceActions;
1608
+ usage: UsageActions;
1609
+ settings: SettingsActions;
1610
+ features: FeatureActions;
1611
+ }
1612
+ interface BuildBaseResult extends ScopedActions {
1613
+ /**
1614
+ * Check authentication. Returns session or null.
1615
+ * Uses the `getSessionId` callback from config.
1616
+ *
1617
+ * ```ts
1618
+ * const session = await auth()
1619
+ * if (!session) return Response.json({ error: "Unauthorized" }, { status: 401 })
1620
+ * ```
1621
+ */
1622
+ auth(): Promise<BuildBaseSession | null>;
1623
+ /**
1624
+ * Create a scoped client bound to a specific session ID.
1625
+ * Returns all the same action modules (workspace, subscription, etc.)
1626
+ * but using the provided session instead of the `getSessionId` callback.
1627
+ *
1628
+ * Use this for frameworks without async request context (Express, Hono, Fastify)
1629
+ * or for background jobs / service accounts.
1630
+ *
1631
+ * @example Express
1632
+ * ```ts
1633
+ * app.get("/api/workspaces", async (req, res) => {
1634
+ * const bb = withSession(req.headers["x-session-id"])
1635
+ * const workspaces = await bb.workspace.list()
1636
+ * res.json(workspaces)
1637
+ * })
1638
+ * ```
1639
+ *
1640
+ * @example Background job
1641
+ * ```ts
1642
+ * const bb = withSession(process.env.SERVICE_SESSION_ID!)
1643
+ * await bb.usage.record(workspaceId, { quotaSlug: "uploads", quantity: 5 })
1644
+ * ```
1645
+ */
1646
+ withSession(sessionId: string): ScopedActions;
1647
+ /** Low-level API classes bound to a specific session */
1648
+ client: {
1649
+ forSession(sessionId: string): {
1650
+ workspace: WorkspaceApi;
1651
+ user: UserApi;
1652
+ settings: SettingsApi;
1653
+ push: PushApi;
1654
+ };
1655
+ };
1656
+ }
1657
+ declare function BuildBase(config: BuildBaseConfig): BuildBaseResult;
2939
1658
 
2940
- export { ApiVersion, AuthStatus, BaseApi, BetaForm, CURRENCY_DISPLAY, CURRENCY_FLAG, PLAN_CURRENCY_CODES, PLAN_CURRENCY_OPTIONS, PUSH_SERVICE_WORKER_SCRIPT, PricingPage, PushNotificationProvider, QuotaUsageContextProvider, SaaSOSProvider, SettingsApi, SubscriptionContextProvider, UserApi, WhenAuthenticated, WhenNoSubscription, WhenNotTrialing, WhenQuotaAvailable, WhenQuotaExhausted, WhenQuotaOverage, WhenQuotaThreshold, WhenRoles, WhenSubscription, WhenSubscriptionToPlans, WhenTrialEnding, WhenTrialing, WhenUnauthenticated, WhenUserFeatureDisabled, WhenUserFeatureEnabled, WhenWorkspaceFeatureDisabled, WhenWorkspaceFeatureEnabled, WhenWorkspaceRoles, WorkspaceApi, WorkspaceSwitcher, calculateBillableSeats, calculateSeatOverageCents, calculateTotalSubscriptionCents, eventEmitter, formatCents, formatOverageRate, formatOverageRateWithLabel, formatQuotaIncludedOverage, formatQuotaWithPrice, getAvailableCurrenciesFromPlans, getBasePriceCents, getBillingIntervalAndCurrencyFromPriceId, getCurrencyFlag, getCurrencySymbol, getDisplayCurrency, getPerSeatPriceCents, getPricingVariant, getQuotaDisplayValue, getQuotaDisplayWithVariant, getQuotaOverageCents, getQuotaUnitLabelFromName, getSeatPricing, getStripePriceIdForInterval, invalidateQuotaUsage, invalidateSubscription, useAllQuotaUsage, useBillingPortal, useCancelSubscription, useCreateCheckoutSession, useInvoice, useInvoices, usePlanGroup, usePlanGroupVersions, usePublicPlanGroupVersion, usePublicPlans, usePushNotifications, useQuotaUsageContext, useQuotaUsageStatus, useRecordUsage, useResumeSubscription, useSaaSAuth, useSaaSOs, useSaaSSettings, useSaaSWorkspaces, useSeatStatus, useSubscription, useSubscriptionContext, useSubscriptionManagement, useTrialStatus, useUpdateSubscription, useUsageLogs, useUserAttributes, useUserFeatures };
2941
- export type { BillingInterval, CheckoutResult, EventData, EventType, FormatQuotaWithPriceOptions, IAllQuotaUsageResponse, IBaseApiConfig, IBasePricing, ICheckoutSessionRequest, ICheckoutSessionResponse, IEventCallbacks, IInvoice, IInvoiceListResponse, IInvoiceResponse, IPlan, IPlanGroup, IPlanGroupInfo, IPlanGroupLatestVersion, IPlanGroupResponse, IPlanGroupVersion, IPlanGroupVersionWithPlans, IPlanGroupVersionsResponse, IPlanVersion, IPlanVersionSummary, IPlanVersionWithPlan, IPricingVariant, IPublicPlanItem, IPublicPlanItemCategory, IPublicPlanVersion, IPublicPlansResponse, IQuotaByInterval, IQuotaIntervalValue, IQuotaOveragePriceIdsByInterval, IQuotaOveragesByInterval, IQuotaUsageStatus, IQuotaUsageStatusResponse, IRecordUsageRequest, IRecordUsageResponse, IStripePricesByInterval, ISubscription, ISubscriptionItem, ISubscriptionResponse, ISubscriptionUpdateRequest, ISubscriptionUpdateResponse, IUsageLogEntry, IUsageLogsQuery, IUsageLogsResponse, InvoiceStatus, OnWorkspaceChangeParams, PlanVersionWithPricingVariants, PricingPageDetails, PricingPageProps, QuotaDisplayValue, QuotaDisplayWithOverage, QuotaUsageContextValue, SeatStatus, SubscriptionContextValue, TrialStatus, UserCreatedEventData, UserUpdatedEventData, WorkspaceChangedEventData, WorkspaceCreatedEventData, WorkspaceDeletedEventData, WorkspaceUpdatedEventData, WorkspaceUserAddedEventData, WorkspaceUserRemovedEventData, WorkspaceUserRoleChangedEventData };
1659
+ export { ApiVersion, AuthApi, AuthStatus, BaseApi, BetaForm, BuildBase, CURRENCY_DISPLAY, CURRENCY_FLAG, EventEmitter, PLAN_CURRENCY_CODES, PLAN_CURRENCY_OPTIONS, PUSH_SERVICE_WORKER_SCRIPT, PushApi, SettingsApi, UserApi, WorkspaceApi, formSchema as betaFormSchema, calculateBillableSeats, calculateSeatOverageCents, calculateTotalSubscriptionCents, countries, uniqueCurrencies as currencies, BuildBase as default, eventEmitter, formatCents, formatOverageRate, formatOverageRateWithLabel, formatQuotaIncludedOverage, formatQuotaWithPrice, getAvailableCurrenciesFromPlans, getBasePriceCents, getBillingIntervalAndCurrencyFromPriceId, getCurrencyFlag, getCurrencySymbol, getDisplayCurrency, getPerSeatPriceCents, getPricingVariant, getQuotaDisplayValue, getQuotaDisplayWithVariant, getQuotaOverageCents, getQuotaUnitLabelFromName, getSeatPricing, getStripePriceIdForInterval, invalidateQuotaUsage, invalidateSubscription, languages, resolveMaxUsers, timezones, validateInvite };
1660
+ export type { BetaFormData, BetaFormResponse, formValuesType as BetaFormValues, BillingInterval, BuildBaseConfig, BuildBaseResult, BuildBaseSession, CheckoutResult, EventData, EventType, FeatureActions, FormatQuotaWithPriceOptions, IAllQuotaUsageResponse, IBaseApiConfig, IBasePricing, IBetaConfig, ICheckoutSessionRequest, ICheckoutSessionResponse, IEventCallbacks, IInvoice, IInvoiceListResponse, IInvoiceResponse, IOsConfig, IOsState, IPlan, IPlanGroup, IPlanGroupInfo, IPlanGroupLatestVersion, IPlanGroupResponse, IPlanGroupVersion, IPlanGroupVersionWithPlans, IPlanGroupVersionsResponse, IPlanVersion, IPlanVersionSummary, IPlanVersionWithPlan, IPricingVariant, IPublicPlanItem, IPublicPlanItemCategory, IPublicPlanVersion, IPublicPlansResponse, IQuotaByInterval, IQuotaIntervalValue, IQuotaOveragePriceIdsByInterval, IQuotaOveragesByInterval, IQuotaUsageStatus, IQuotaUsageStatusResponse, IRecordUsageRequest, IRecordUsageResponse, IStripePricesByInterval, ISubscription, ISubscriptionItem, ISubscriptionResponse, ISubscriptionUpdateRequest, ISubscriptionUpdateResponse, IUsageLogEntry, IUsageLogsQuery, IUsageLogsResponse, InviteBlockReason, InviteValidation, InvoiceActions, InvoiceStatus, MaxUsersConfig, OnWorkspaceChangeParams, PlanActions, PlanVersionWithPricingVariants, QuotaDisplayValue, QuotaDisplayWithOverage, ScopedActions, SettingsActions, SubscriptionActions, UsageActions, UserActions, UserCreatedEventData, UserUpdatedEventData, WorkspaceActions, WorkspaceChangedEventData, WorkspaceCreatedEventData, WorkspaceDeletedEventData, WorkspaceUpdatedEventData, WorkspaceUserAddedEventData, WorkspaceUserRemovedEventData, WorkspaceUserRoleChangedEventData };