@habeetat/sdk-react 0.1.0-dev.20260323155801.0ae6298

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # @habeetat/sdk-react
2
+
3
+ Habeetat Platform SDK for React applications.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @habeetat/sdk-react @logto/react
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```tsx
14
+ import { LogtoProvider } from '@logto/react';
15
+ import { HabeetatProvider, useHabeetat, usePermissions, useFeatures } from '@habeetat/sdk-react';
16
+
17
+ // Wrap your app with providers
18
+ function App() {
19
+ return (
20
+ <LogtoProvider config={logtoConfig}>
21
+ <HabeetatProvider
22
+ platformUrl="https://api.habeetat.io"
23
+ tenantSlug="acme-corp"
24
+ >
25
+ <MyApp />
26
+ </HabeetatProvider>
27
+ </LogtoProvider>
28
+ );
29
+ }
30
+
31
+ // Use hooks in components
32
+ function Dashboard() {
33
+ const { context, isLoading } = useHabeetat();
34
+ const { hasPermission } = usePermissions();
35
+ const { isEnabled } = useFeatures();
36
+
37
+ if (isLoading) return <Spinner />;
38
+
39
+ return (
40
+ <div>
41
+ <h1>Welcome, {context?.user.name}</h1>
42
+ {hasPermission('contacts:read') && <ContactsList />}
43
+ {isEnabled('crm.deals.enabled') && <DealsWidget />}
44
+ </div>
45
+ );
46
+ }
47
+ ```
48
+
49
+ ## Hooks
50
+
51
+ ### useHabeetat
52
+ Main hook for accessing SDK context.
53
+
54
+ ### usePermissions
55
+ Hook for permission checks.
56
+
57
+ ### useFeatures
58
+ Hook for feature flag checks.
59
+
60
+ ### useSubscription
61
+ Hook for subscription and plan info.
62
+
63
+ ## Components
64
+
65
+ ### RequirePermission
66
+ Renders children only if user has permission.
67
+
68
+ ### RequireFeature
69
+ Renders children only if feature is enabled.
70
+
71
+ ## License
72
+
73
+ MIT
@@ -0,0 +1,217 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
3
+ import { ReactNode } from 'react';
4
+ import * as _habeetat_sdk_core from '@habeetat/sdk-core';
5
+ import { SdkContext, SdkFeaturesState, SdkSubscription } from '@habeetat/sdk-core';
6
+ export { SdkContext, SdkFeaturesState, SdkPlan, SdkSubscription, SdkTenantContext, SdkUserContext } from '@habeetat/sdk-core';
7
+
8
+ /**
9
+ * Props for HabeetatProvider
10
+ */
11
+ interface HabeetatProviderProps {
12
+ /** Platform API base URL (SDK endpoints) */
13
+ platformUrl: string;
14
+ /** Logto API resource identifier (for token requests) */
15
+ logtoResource?: string;
16
+ /** App ID for this application */
17
+ appId?: string;
18
+ /** Tenant slug (if known) */
19
+ tenantSlug?: string;
20
+ /** Whether to auto-fetch context on mount */
21
+ autoFetch?: boolean;
22
+ /** Children components */
23
+ children: ReactNode;
24
+ }
25
+ /**
26
+ * HabeetatProvider - Main provider for Habeetat SDK in React apps
27
+ *
28
+ * Must be used inside LogtoProvider from @logto/react
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * import { LogtoProvider } from '@logto/react';
33
+ * import { HabeetatProvider } from '@habeetat/sdk-react';
34
+ *
35
+ * function App() {
36
+ * return (
37
+ * <LogtoProvider config={logtoConfig}>
38
+ * <HabeetatProvider
39
+ * platformUrl="https://api.habeetat.io"
40
+ * tenantSlug="acme-corp"
41
+ * >
42
+ * <MyApp />
43
+ * </HabeetatProvider>
44
+ * </LogtoProvider>
45
+ * );
46
+ * }
47
+ * ```
48
+ */
49
+ declare function HabeetatProvider({ platformUrl, logtoResource, appId, tenantSlug, autoFetch, children, }: HabeetatProviderProps): react_jsx_runtime.JSX.Element;
50
+
51
+ /**
52
+ * Habeetat SDK state
53
+ */
54
+ interface HabeetatState {
55
+ /** Whether the SDK is loading initial data */
56
+ isLoading: boolean;
57
+ /** Error if any occurred */
58
+ error: Error | null;
59
+ /** SDK context (user, tenant, permissions) */
60
+ context: SdkContext | null;
61
+ /** Feature flags */
62
+ features: SdkFeaturesState | null;
63
+ /** Subscription info */
64
+ subscription: SdkSubscription | null;
65
+ }
66
+ /**
67
+ * Habeetat SDK context value
68
+ */
69
+ interface HabeetatContextValue extends HabeetatState {
70
+ /** Refresh context from server */
71
+ refreshContext: () => Promise<void>;
72
+ /** Refresh features from server */
73
+ refreshFeatures: () => Promise<void>;
74
+ /** Refresh subscription from server */
75
+ refreshSubscription: () => Promise<void>;
76
+ /** Check if user has permission */
77
+ hasPermission: (permission: string) => boolean;
78
+ /** Check if user has any of the permissions */
79
+ hasAnyPermission: (permissions: string[]) => boolean;
80
+ /** Check if user has all permissions */
81
+ hasAllPermissions: (permissions: string[]) => boolean;
82
+ /** Check if feature is enabled */
83
+ isFeatureEnabled: (key: string) => boolean;
84
+ /** Get access token for API calls */
85
+ getAccessToken: () => Promise<string | null>;
86
+ }
87
+ /**
88
+ * Habeetat React Context
89
+ */
90
+ declare const HabeetatContext: react.Context<HabeetatContextValue>;
91
+
92
+ /**
93
+ * Hook to access Habeetat SDK context
94
+ *
95
+ * @example
96
+ * ```tsx
97
+ * function MyComponent() {
98
+ * const { context, isLoading, error } = useHabeetat();
99
+ *
100
+ * if (isLoading) return <Spinner />;
101
+ * if (error) return <Error message={error.message} />;
102
+ *
103
+ * return <div>Hello, {context?.user.name}</div>;
104
+ * }
105
+ * ```
106
+ */
107
+ declare function useHabeetat(): HabeetatContextValue;
108
+
109
+ /**
110
+ * Hook for permission checks
111
+ *
112
+ * @example
113
+ * ```tsx
114
+ * function ContactsPage() {
115
+ * const { hasPermission, hasAnyPermission } = usePermissions();
116
+ *
117
+ * const canRead = hasPermission('contacts:read');
118
+ * const canWrite = hasPermission('contacts:write');
119
+ * const canManage = hasAnyPermission(['contacts:delete', 'contacts:admin']);
120
+ *
121
+ * return (
122
+ * <div>
123
+ * {canRead && <ContactsList />}
124
+ * {canWrite && <AddContactButton />}
125
+ * {canManage && <ManageContactsButton />}
126
+ * </div>
127
+ * );
128
+ * }
129
+ * ```
130
+ */
131
+ declare function usePermissions(): {
132
+ /** All user permissions */
133
+ permissions: string[];
134
+ /** All user roles */
135
+ roles: string[];
136
+ /** Check if user has a specific permission */
137
+ hasPermission: (permission: string) => boolean;
138
+ /** Check if user has any of the specified permissions */
139
+ hasAnyPermission: (permissions: string[]) => boolean;
140
+ /** Check if user has all of the specified permissions */
141
+ hasAllPermissions: (permissions: string[]) => boolean;
142
+ };
143
+
144
+ /**
145
+ * Hook for feature flag checks
146
+ *
147
+ * @example
148
+ * ```tsx
149
+ * function DealsPage() {
150
+ * const { isEnabled, features } = useFeatures();
151
+ *
152
+ * if (!isEnabled('crm.deals.enabled')) {
153
+ * return <UpgradePrompt feature="Deals" />;
154
+ * }
155
+ *
156
+ * return <DealsList />;
157
+ * }
158
+ * ```
159
+ */
160
+ declare function useFeatures(): {
161
+ /** All feature flags */
162
+ features: Record<string, boolean>;
163
+ /** Feature source (plan, tenant, etc.) */
164
+ source: _habeetat_sdk_core.FeatureFlagSource | undefined;
165
+ /** Plan code if source is plan */
166
+ planCode: string | undefined;
167
+ /** Check if a feature is enabled */
168
+ isEnabled: (key: string) => boolean;
169
+ /** Refresh features from server */
170
+ refresh: () => Promise<void>;
171
+ };
172
+
173
+ /**
174
+ * Hook for subscription and plan info
175
+ */
176
+ declare function useSubscription(): {
177
+ /** Current subscription */
178
+ subscription: _habeetat_sdk_core.SdkSubscription | null;
179
+ /** Current plan */
180
+ plan: _habeetat_sdk_core.SdkPlan | undefined;
181
+ /** Plan limits */
182
+ limits: _habeetat_sdk_core.SdkPlanLimits;
183
+ /** Current usage */
184
+ usage: _habeetat_sdk_core.SdkPlanUsage;
185
+ /** Subscription status */
186
+ status: _habeetat_sdk_core.SubscriptionStatus | undefined;
187
+ /** Check if subscription is active */
188
+ isActive: boolean;
189
+ /** Check if in trial */
190
+ isTrialing: boolean;
191
+ /** Check limit */
192
+ checkLimit: (key: string, increment?: number) => boolean;
193
+ /** Refresh subscription */
194
+ refresh: () => Promise<void>;
195
+ };
196
+
197
+ interface RequirePermissionProps {
198
+ permission: string;
199
+ children: ReactNode;
200
+ fallback?: ReactNode;
201
+ }
202
+ /**
203
+ * Component that renders children only if user has the required permission
204
+ */
205
+ declare function RequirePermission({ permission, children, fallback }: RequirePermissionProps): react_jsx_runtime.JSX.Element;
206
+
207
+ interface RequireFeatureProps {
208
+ flag: string;
209
+ children: ReactNode;
210
+ fallback?: ReactNode;
211
+ }
212
+ /**
213
+ * Component that renders children only if the feature flag is enabled
214
+ */
215
+ declare function RequireFeature({ flag, children, fallback }: RequireFeatureProps): react_jsx_runtime.JSX.Element;
216
+
217
+ export { HabeetatContext, type HabeetatContextValue, HabeetatProvider, type HabeetatProviderProps, type HabeetatState, RequireFeature, RequirePermission, useFeatures, useHabeetat, usePermissions, useSubscription };
@@ -0,0 +1,217 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
3
+ import { ReactNode } from 'react';
4
+ import * as _habeetat_sdk_core from '@habeetat/sdk-core';
5
+ import { SdkContext, SdkFeaturesState, SdkSubscription } from '@habeetat/sdk-core';
6
+ export { SdkContext, SdkFeaturesState, SdkPlan, SdkSubscription, SdkTenantContext, SdkUserContext } from '@habeetat/sdk-core';
7
+
8
+ /**
9
+ * Props for HabeetatProvider
10
+ */
11
+ interface HabeetatProviderProps {
12
+ /** Platform API base URL (SDK endpoints) */
13
+ platformUrl: string;
14
+ /** Logto API resource identifier (for token requests) */
15
+ logtoResource?: string;
16
+ /** App ID for this application */
17
+ appId?: string;
18
+ /** Tenant slug (if known) */
19
+ tenantSlug?: string;
20
+ /** Whether to auto-fetch context on mount */
21
+ autoFetch?: boolean;
22
+ /** Children components */
23
+ children: ReactNode;
24
+ }
25
+ /**
26
+ * HabeetatProvider - Main provider for Habeetat SDK in React apps
27
+ *
28
+ * Must be used inside LogtoProvider from @logto/react
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * import { LogtoProvider } from '@logto/react';
33
+ * import { HabeetatProvider } from '@habeetat/sdk-react';
34
+ *
35
+ * function App() {
36
+ * return (
37
+ * <LogtoProvider config={logtoConfig}>
38
+ * <HabeetatProvider
39
+ * platformUrl="https://api.habeetat.io"
40
+ * tenantSlug="acme-corp"
41
+ * >
42
+ * <MyApp />
43
+ * </HabeetatProvider>
44
+ * </LogtoProvider>
45
+ * );
46
+ * }
47
+ * ```
48
+ */
49
+ declare function HabeetatProvider({ platformUrl, logtoResource, appId, tenantSlug, autoFetch, children, }: HabeetatProviderProps): react_jsx_runtime.JSX.Element;
50
+
51
+ /**
52
+ * Habeetat SDK state
53
+ */
54
+ interface HabeetatState {
55
+ /** Whether the SDK is loading initial data */
56
+ isLoading: boolean;
57
+ /** Error if any occurred */
58
+ error: Error | null;
59
+ /** SDK context (user, tenant, permissions) */
60
+ context: SdkContext | null;
61
+ /** Feature flags */
62
+ features: SdkFeaturesState | null;
63
+ /** Subscription info */
64
+ subscription: SdkSubscription | null;
65
+ }
66
+ /**
67
+ * Habeetat SDK context value
68
+ */
69
+ interface HabeetatContextValue extends HabeetatState {
70
+ /** Refresh context from server */
71
+ refreshContext: () => Promise<void>;
72
+ /** Refresh features from server */
73
+ refreshFeatures: () => Promise<void>;
74
+ /** Refresh subscription from server */
75
+ refreshSubscription: () => Promise<void>;
76
+ /** Check if user has permission */
77
+ hasPermission: (permission: string) => boolean;
78
+ /** Check if user has any of the permissions */
79
+ hasAnyPermission: (permissions: string[]) => boolean;
80
+ /** Check if user has all permissions */
81
+ hasAllPermissions: (permissions: string[]) => boolean;
82
+ /** Check if feature is enabled */
83
+ isFeatureEnabled: (key: string) => boolean;
84
+ /** Get access token for API calls */
85
+ getAccessToken: () => Promise<string | null>;
86
+ }
87
+ /**
88
+ * Habeetat React Context
89
+ */
90
+ declare const HabeetatContext: react.Context<HabeetatContextValue>;
91
+
92
+ /**
93
+ * Hook to access Habeetat SDK context
94
+ *
95
+ * @example
96
+ * ```tsx
97
+ * function MyComponent() {
98
+ * const { context, isLoading, error } = useHabeetat();
99
+ *
100
+ * if (isLoading) return <Spinner />;
101
+ * if (error) return <Error message={error.message} />;
102
+ *
103
+ * return <div>Hello, {context?.user.name}</div>;
104
+ * }
105
+ * ```
106
+ */
107
+ declare function useHabeetat(): HabeetatContextValue;
108
+
109
+ /**
110
+ * Hook for permission checks
111
+ *
112
+ * @example
113
+ * ```tsx
114
+ * function ContactsPage() {
115
+ * const { hasPermission, hasAnyPermission } = usePermissions();
116
+ *
117
+ * const canRead = hasPermission('contacts:read');
118
+ * const canWrite = hasPermission('contacts:write');
119
+ * const canManage = hasAnyPermission(['contacts:delete', 'contacts:admin']);
120
+ *
121
+ * return (
122
+ * <div>
123
+ * {canRead && <ContactsList />}
124
+ * {canWrite && <AddContactButton />}
125
+ * {canManage && <ManageContactsButton />}
126
+ * </div>
127
+ * );
128
+ * }
129
+ * ```
130
+ */
131
+ declare function usePermissions(): {
132
+ /** All user permissions */
133
+ permissions: string[];
134
+ /** All user roles */
135
+ roles: string[];
136
+ /** Check if user has a specific permission */
137
+ hasPermission: (permission: string) => boolean;
138
+ /** Check if user has any of the specified permissions */
139
+ hasAnyPermission: (permissions: string[]) => boolean;
140
+ /** Check if user has all of the specified permissions */
141
+ hasAllPermissions: (permissions: string[]) => boolean;
142
+ };
143
+
144
+ /**
145
+ * Hook for feature flag checks
146
+ *
147
+ * @example
148
+ * ```tsx
149
+ * function DealsPage() {
150
+ * const { isEnabled, features } = useFeatures();
151
+ *
152
+ * if (!isEnabled('crm.deals.enabled')) {
153
+ * return <UpgradePrompt feature="Deals" />;
154
+ * }
155
+ *
156
+ * return <DealsList />;
157
+ * }
158
+ * ```
159
+ */
160
+ declare function useFeatures(): {
161
+ /** All feature flags */
162
+ features: Record<string, boolean>;
163
+ /** Feature source (plan, tenant, etc.) */
164
+ source: _habeetat_sdk_core.FeatureFlagSource | undefined;
165
+ /** Plan code if source is plan */
166
+ planCode: string | undefined;
167
+ /** Check if a feature is enabled */
168
+ isEnabled: (key: string) => boolean;
169
+ /** Refresh features from server */
170
+ refresh: () => Promise<void>;
171
+ };
172
+
173
+ /**
174
+ * Hook for subscription and plan info
175
+ */
176
+ declare function useSubscription(): {
177
+ /** Current subscription */
178
+ subscription: _habeetat_sdk_core.SdkSubscription | null;
179
+ /** Current plan */
180
+ plan: _habeetat_sdk_core.SdkPlan | undefined;
181
+ /** Plan limits */
182
+ limits: _habeetat_sdk_core.SdkPlanLimits;
183
+ /** Current usage */
184
+ usage: _habeetat_sdk_core.SdkPlanUsage;
185
+ /** Subscription status */
186
+ status: _habeetat_sdk_core.SubscriptionStatus | undefined;
187
+ /** Check if subscription is active */
188
+ isActive: boolean;
189
+ /** Check if in trial */
190
+ isTrialing: boolean;
191
+ /** Check limit */
192
+ checkLimit: (key: string, increment?: number) => boolean;
193
+ /** Refresh subscription */
194
+ refresh: () => Promise<void>;
195
+ };
196
+
197
+ interface RequirePermissionProps {
198
+ permission: string;
199
+ children: ReactNode;
200
+ fallback?: ReactNode;
201
+ }
202
+ /**
203
+ * Component that renders children only if user has the required permission
204
+ */
205
+ declare function RequirePermission({ permission, children, fallback }: RequirePermissionProps): react_jsx_runtime.JSX.Element;
206
+
207
+ interface RequireFeatureProps {
208
+ flag: string;
209
+ children: ReactNode;
210
+ fallback?: ReactNode;
211
+ }
212
+ /**
213
+ * Component that renders children only if the feature flag is enabled
214
+ */
215
+ declare function RequireFeature({ flag, children, fallback }: RequireFeatureProps): react_jsx_runtime.JSX.Element;
216
+
217
+ export { HabeetatContext, type HabeetatContextValue, HabeetatProvider, type HabeetatProviderProps, type HabeetatState, RequireFeature, RequirePermission, useFeatures, useHabeetat, usePermissions, useSubscription };
package/dist/index.js ADDED
@@ -0,0 +1,269 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var react$1 = require('@logto/react');
5
+ var sdkCore = require('@habeetat/sdk-core');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+
8
+ // src/provider/HabeetatProvider.tsx
9
+ var defaultContextValue = {
10
+ isLoading: true,
11
+ error: null,
12
+ context: null,
13
+ features: null,
14
+ subscription: null,
15
+ refreshContext: async () => {
16
+ },
17
+ refreshFeatures: async () => {
18
+ },
19
+ refreshSubscription: async () => {
20
+ },
21
+ hasPermission: () => false,
22
+ hasAnyPermission: () => false,
23
+ hasAllPermissions: () => false,
24
+ isFeatureEnabled: () => false,
25
+ getAccessToken: async () => null
26
+ };
27
+ var HabeetatContext = react.createContext(defaultContextValue);
28
+ function HabeetatProvider({
29
+ platformUrl,
30
+ logtoResource,
31
+ appId,
32
+ tenantSlug,
33
+ autoFetch = true,
34
+ children
35
+ }) {
36
+ const { isAuthenticated, getAccessToken } = react$1.useLogto();
37
+ const tokenResource = logtoResource || platformUrl;
38
+ const [state, setState] = react.useState({
39
+ isLoading: true,
40
+ error: null,
41
+ context: null,
42
+ features: null,
43
+ subscription: null
44
+ });
45
+ const apiUrl = react.useMemo(() => platformUrl.replace(/\/$/, ""), [platformUrl]);
46
+ const fetchApi = react.useCallback(async (endpoint) => {
47
+ let token;
48
+ try {
49
+ token = await getAccessToken(tokenResource);
50
+ console.log("[HabeetatSDK] Got token for resource:", tokenResource, token ? "OK" : "EMPTY");
51
+ } catch (err) {
52
+ console.error("[HabeetatSDK] Failed to get token for resource:", tokenResource, err);
53
+ throw new Error(`Failed to get access token: ${err}`);
54
+ }
55
+ if (!token) {
56
+ console.error("[HabeetatSDK] No token returned for resource:", tokenResource);
57
+ throw new Error("No access token available");
58
+ }
59
+ const url = new URL(endpoint, apiUrl);
60
+ if (tenantSlug) {
61
+ url.searchParams.set("tenantSlug", tenantSlug);
62
+ }
63
+ if (appId) {
64
+ url.searchParams.set("appId", appId);
65
+ }
66
+ const response = await fetch(url.toString(), {
67
+ headers: {
68
+ "Authorization": `Bearer ${token}`,
69
+ "Content-Type": "application/json"
70
+ }
71
+ });
72
+ if (!response.ok) {
73
+ throw new Error(`API error: ${response.status}`);
74
+ }
75
+ return response.json();
76
+ }, [apiUrl, tokenResource, tenantSlug, appId, getAccessToken]);
77
+ const refreshContext = react.useCallback(async () => {
78
+ try {
79
+ const context = await fetchApi(sdkCore.SDK_ENDPOINTS.CONTEXT);
80
+ setState((prev) => ({ ...prev, context, error: null }));
81
+ } catch (error) {
82
+ setState((prev) => ({ ...prev, error }));
83
+ }
84
+ }, [fetchApi]);
85
+ const refreshFeatures = react.useCallback(async () => {
86
+ try {
87
+ const features = await fetchApi(sdkCore.SDK_ENDPOINTS.FEATURES);
88
+ setState((prev) => ({ ...prev, features, error: null }));
89
+ } catch (error) {
90
+ setState((prev) => ({ ...prev, error }));
91
+ }
92
+ }, [fetchApi]);
93
+ const refreshSubscription = react.useCallback(async () => {
94
+ try {
95
+ const subscription = await fetchApi(sdkCore.SDK_ENDPOINTS.SUBSCRIPTION);
96
+ setState((prev) => ({ ...prev, subscription, error: null }));
97
+ } catch (error) {
98
+ setState((prev) => ({ ...prev, error }));
99
+ }
100
+ }, [fetchApi]);
101
+ const hasPermission = react.useCallback((permission) => {
102
+ return state.context?.permissions?.includes(permission) ?? false;
103
+ }, [state.context?.permissions]);
104
+ const hasAnyPermission = react.useCallback((permissions) => {
105
+ return permissions.some((p) => state.context?.permissions?.includes(p));
106
+ }, [state.context?.permissions]);
107
+ const hasAllPermissions = react.useCallback((permissions) => {
108
+ return permissions.every((p) => state.context?.permissions?.includes(p));
109
+ }, [state.context?.permissions]);
110
+ const isFeatureEnabled = react.useCallback((key) => {
111
+ return state.features?.features?.[key] ?? false;
112
+ }, [state.features?.features]);
113
+ const getToken = react.useCallback(async () => {
114
+ try {
115
+ const token = await getAccessToken(tokenResource);
116
+ return token ?? null;
117
+ } catch {
118
+ return null;
119
+ }
120
+ }, [getAccessToken, tokenResource]);
121
+ react.useEffect(() => {
122
+ if (!isAuthenticated || !autoFetch) {
123
+ setState((prev) => ({ ...prev, isLoading: false }));
124
+ return;
125
+ }
126
+ const fetchAll = async () => {
127
+ setState((prev) => ({ ...prev, isLoading: true }));
128
+ try {
129
+ const [context, features, subscription] = await Promise.all([
130
+ fetchApi(sdkCore.SDK_ENDPOINTS.CONTEXT).catch(() => null),
131
+ fetchApi(sdkCore.SDK_ENDPOINTS.FEATURES).catch(() => null),
132
+ fetchApi(sdkCore.SDK_ENDPOINTS.SUBSCRIPTION).catch(() => null)
133
+ ]);
134
+ setState({
135
+ isLoading: false,
136
+ error: null,
137
+ context,
138
+ features,
139
+ subscription
140
+ });
141
+ } catch (error) {
142
+ setState((prev) => ({
143
+ ...prev,
144
+ isLoading: false,
145
+ error
146
+ }));
147
+ }
148
+ };
149
+ fetchAll();
150
+ }, [isAuthenticated, autoFetch, fetchApi]);
151
+ const contextValue = react.useMemo(() => ({
152
+ ...state,
153
+ refreshContext,
154
+ refreshFeatures,
155
+ refreshSubscription,
156
+ hasPermission,
157
+ hasAnyPermission,
158
+ hasAllPermissions,
159
+ isFeatureEnabled,
160
+ getAccessToken: getToken
161
+ }), [
162
+ state,
163
+ refreshContext,
164
+ refreshFeatures,
165
+ refreshSubscription,
166
+ hasPermission,
167
+ hasAnyPermission,
168
+ hasAllPermissions,
169
+ isFeatureEnabled,
170
+ getToken
171
+ ]);
172
+ return /* @__PURE__ */ jsxRuntime.jsx(HabeetatContext.Provider, { value: contextValue, children });
173
+ }
174
+ function useHabeetat() {
175
+ const context = react.useContext(HabeetatContext);
176
+ if (!context) {
177
+ throw new Error("useHabeetat must be used within a HabeetatProvider");
178
+ }
179
+ return context;
180
+ }
181
+
182
+ // src/hooks/usePermissions.ts
183
+ function usePermissions() {
184
+ const { context, hasPermission, hasAnyPermission, hasAllPermissions } = useHabeetat();
185
+ return {
186
+ /** All user permissions */
187
+ permissions: context?.permissions ?? [],
188
+ /** All user roles */
189
+ roles: context?.roles ?? [],
190
+ /** Check if user has a specific permission */
191
+ hasPermission,
192
+ /** Check if user has any of the specified permissions */
193
+ hasAnyPermission,
194
+ /** Check if user has all of the specified permissions */
195
+ hasAllPermissions
196
+ };
197
+ }
198
+
199
+ // src/hooks/useFeatures.ts
200
+ function useFeatures() {
201
+ const { features, isFeatureEnabled, refreshFeatures } = useHabeetat();
202
+ return {
203
+ /** All feature flags */
204
+ features: features?.features ?? {},
205
+ /** Feature source (plan, tenant, etc.) */
206
+ source: features?.source,
207
+ /** Plan code if source is plan */
208
+ planCode: features?.planCode,
209
+ /** Check if a feature is enabled */
210
+ isEnabled: isFeatureEnabled,
211
+ /** Refresh features from server */
212
+ refresh: refreshFeatures
213
+ };
214
+ }
215
+
216
+ // src/hooks/useSubscription.ts
217
+ function useSubscription() {
218
+ const { subscription, refreshSubscription } = useHabeetat();
219
+ return {
220
+ /** Current subscription */
221
+ subscription,
222
+ /** Current plan */
223
+ plan: subscription?.plan,
224
+ /** Plan limits */
225
+ limits: subscription?.limits ?? {},
226
+ /** Current usage */
227
+ usage: subscription?.usage ?? {},
228
+ /** Subscription status */
229
+ status: subscription?.status,
230
+ /** Check if subscription is active */
231
+ isActive: subscription?.status === "active" || subscription?.status === "trialing",
232
+ /** Check if in trial */
233
+ isTrialing: subscription?.status === "trialing",
234
+ /** Check limit */
235
+ checkLimit: (key, increment = 0) => {
236
+ const limit = subscription?.limits?.[key];
237
+ const current = subscription?.usage?.[key] ?? 0;
238
+ if (limit === void 0) return true;
239
+ return current + increment <= limit;
240
+ },
241
+ /** Refresh subscription */
242
+ refresh: refreshSubscription
243
+ };
244
+ }
245
+ function RequirePermission({ permission, children, fallback = null }) {
246
+ const { hasPermission } = usePermissions();
247
+ if (!hasPermission(permission)) {
248
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback });
249
+ }
250
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
251
+ }
252
+ function RequireFeature({ flag, children, fallback = null }) {
253
+ const { isEnabled } = useFeatures();
254
+ if (!isEnabled(flag)) {
255
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback });
256
+ }
257
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
258
+ }
259
+
260
+ exports.HabeetatContext = HabeetatContext;
261
+ exports.HabeetatProvider = HabeetatProvider;
262
+ exports.RequireFeature = RequireFeature;
263
+ exports.RequirePermission = RequirePermission;
264
+ exports.useFeatures = useFeatures;
265
+ exports.useHabeetat = useHabeetat;
266
+ exports.usePermissions = usePermissions;
267
+ exports.useSubscription = useSubscription;
268
+ //# sourceMappingURL=index.js.map
269
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/context/HabeetatContext.ts","../src/provider/HabeetatProvider.tsx","../src/hooks/useHabeetat.ts","../src/hooks/usePermissions.ts","../src/hooks/useFeatures.ts","../src/hooks/useSubscription.ts","../src/components/RequirePermission.tsx","../src/components/RequireFeature.tsx"],"names":["createContext","useLogto","useState","useMemo","useCallback","SDK_ENDPOINTS","useEffect","useContext","jsx","Fragment"],"mappings":";;;;;;;;AA4CA,IAAM,mBAAA,GAA4C;AAAA,EAChD,SAAA,EAAW,IAAA;AAAA,EACX,KAAA,EAAO,IAAA;AAAA,EACP,OAAA,EAAS,IAAA;AAAA,EACT,QAAA,EAAU,IAAA;AAAA,EACV,YAAA,EAAc,IAAA;AAAA,EACd,gBAAgB,YAAY;AAAA,EAAC,CAAA;AAAA,EAC7B,iBAAiB,YAAY;AAAA,EAAC,CAAA;AAAA,EAC9B,qBAAqB,YAAY;AAAA,EAAC,CAAA;AAAA,EAClC,eAAe,MAAM,KAAA;AAAA,EACrB,kBAAkB,MAAM,KAAA;AAAA,EACxB,mBAAmB,MAAM,KAAA;AAAA,EACzB,kBAAkB,MAAM,KAAA;AAAA,EACxB,gBAAgB,YAAY;AAC9B,CAAA;AAKO,IAAM,eAAA,GAAkBA,oBAAoC,mBAAmB;ACf/E,SAAS,gBAAA,CAAiB;AAAA,EAC/B,WAAA;AAAA,EACA,aAAA;AAAA,EACA,KAAA;AAAA,EACA,UAAA;AAAA,EACA,SAAA,GAAY,IAAA;AAAA,EACZ;AACF,CAAA,EAA0B;AACxB,EAAA,MAAM,EAAE,eAAA,EAAiB,cAAA,EAAe,GAAIC,gBAAA,EAAS;AAGrD,EAAA,MAAM,gBAAgB,aAAA,IAAiB,WAAA;AAEvC,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIC,cAAA,CAAwB;AAAA,IAChD,SAAA,EAAW,IAAA;AAAA,IACX,KAAA,EAAO,IAAA;AAAA,IACP,OAAA,EAAS,IAAA;AAAA,IACT,QAAA,EAAU,IAAA;AAAA,IACV,YAAA,EAAc;AAAA,GACf,CAAA;AAGD,EAAA,MAAM,MAAA,GAASC,aAAA,CAAQ,MAAM,WAAA,CAAY,OAAA,CAAQ,OAAO,EAAE,CAAA,EAAG,CAAC,WAAW,CAAC,CAAA;AAG1E,EAAA,MAAM,QAAA,GAAWC,iBAAA,CAAY,OAAW,QAAA,KAAiC;AAEvE,IAAA,IAAI,KAAA;AACJ,IAAA,IAAI;AACF,MAAA,KAAA,GAAQ,MAAM,eAAe,aAAa,CAAA;AAC1C,MAAA,OAAA,CAAQ,GAAA,CAAI,uCAAA,EAAyC,aAAA,EAAe,KAAA,GAAQ,OAAO,OAAO,CAAA;AAAA,IAC5F,SAAS,GAAA,EAAK;AACZ,MAAA,OAAA,CAAQ,KAAA,CAAM,iDAAA,EAAmD,aAAA,EAAe,GAAG,CAAA;AACnF,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,4BAAA,EAA+B,GAAG,CAAA,CAAE,CAAA;AAAA,IACtD;AAEA,IAAA,IAAI,CAAC,KAAA,EAAO;AACV,MAAA,OAAA,CAAQ,KAAA,CAAM,iDAAiD,aAAa,CAAA;AAC5E,MAAA,MAAM,IAAI,MAAM,2BAA2B,CAAA;AAAA,IAC7C;AAEA,IAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,QAAA,EAAU,MAAM,CAAA;AACpC,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,YAAA,EAAc,UAAU,CAAA;AAAA,IAC/C;AACA,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,OAAA,EAAS,KAAK,CAAA;AAAA,IACrC;AAEA,IAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,CAAI,UAAS,EAAG;AAAA,MAC3C,OAAA,EAAS;AAAA,QACP,eAAA,EAAiB,UAAU,KAAK,CAAA,CAAA;AAAA,QAChC,cAAA,EAAgB;AAAA;AAClB,KACD,CAAA;AAED,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,WAAA,EAAc,QAAA,CAAS,MAAM,CAAA,CAAE,CAAA;AAAA,IACjD;AAEA,IAAA,OAAO,SAAS,IAAA,EAAK;AAAA,EACvB,GAAG,CAAC,MAAA,EAAQ,eAAe,UAAA,EAAY,KAAA,EAAO,cAAc,CAAC,CAAA;AAG7D,EAAA,MAAM,cAAA,GAAiBA,kBAAY,YAAY;AAC7C,IAAA,IAAI;AACF,MAAA,MAAM,OAAA,GAAU,MAAM,QAAA,CAAqBC,qBAAA,CAAc,OAAO,CAAA;AAChE,MAAA,QAAA,CAAS,WAAS,EAAE,GAAG,MAAM,OAAA,EAAS,KAAA,EAAO,MAAK,CAAE,CAAA;AAAA,IACtD,SAAS,KAAA,EAAO;AACd,MAAA,QAAA,CAAS,CAAA,IAAA,MAAS,EAAE,GAAG,IAAA,EAAM,OAAsB,CAAE,CAAA;AAAA,IACvD;AAAA,EACF,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAGb,EAAA,MAAM,eAAA,GAAkBD,kBAAY,YAAY;AAC9C,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,QAAA,CAA2BC,qBAAA,CAAc,QAAQ,CAAA;AACxE,MAAA,QAAA,CAAS,WAAS,EAAE,GAAG,MAAM,QAAA,EAAU,KAAA,EAAO,MAAK,CAAE,CAAA;AAAA,IACvD,SAAS,KAAA,EAAO;AACd,MAAA,QAAA,CAAS,CAAA,IAAA,MAAS,EAAE,GAAG,IAAA,EAAM,OAAsB,CAAE,CAAA;AAAA,IACvD;AAAA,EACF,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAGb,EAAA,MAAM,mBAAA,GAAsBD,kBAAY,YAAY;AAClD,IAAA,IAAI;AACF,MAAA,MAAM,YAAA,GAAe,MAAM,QAAA,CAA0BC,qBAAA,CAAc,YAAY,CAAA;AAC/E,MAAA,QAAA,CAAS,WAAS,EAAE,GAAG,MAAM,YAAA,EAAc,KAAA,EAAO,MAAK,CAAE,CAAA;AAAA,IAC3D,SAAS,KAAA,EAAO;AACd,MAAA,QAAA,CAAS,CAAA,IAAA,MAAS,EAAE,GAAG,IAAA,EAAM,OAAsB,CAAE,CAAA;AAAA,IACvD;AAAA,EACF,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAGb,EAAA,MAAM,aAAA,GAAgBD,iBAAA,CAAY,CAAC,UAAA,KAAgC;AACjE,IAAA,OAAO,KAAA,CAAM,OAAA,EAAS,WAAA,EAAa,QAAA,CAAS,UAAU,CAAA,IAAK,KAAA;AAAA,EAC7D,CAAA,EAAG,CAAC,KAAA,CAAM,OAAA,EAAS,WAAW,CAAC,CAAA;AAE/B,EAAA,MAAM,gBAAA,GAAmBA,iBAAA,CAAY,CAAC,WAAA,KAAmC;AACvE,IAAA,OAAO,WAAA,CAAY,KAAK,CAAA,CAAA,KAAK,KAAA,CAAM,SAAS,WAAA,EAAa,QAAA,CAAS,CAAC,CAAC,CAAA;AAAA,EACtE,CAAA,EAAG,CAAC,KAAA,CAAM,OAAA,EAAS,WAAW,CAAC,CAAA;AAE/B,EAAA,MAAM,iBAAA,GAAoBA,iBAAA,CAAY,CAAC,WAAA,KAAmC;AACxE,IAAA,OAAO,WAAA,CAAY,MAAM,CAAA,CAAA,KAAK,KAAA,CAAM,SAAS,WAAA,EAAa,QAAA,CAAS,CAAC,CAAC,CAAA;AAAA,EACvE,CAAA,EAAG,CAAC,KAAA,CAAM,OAAA,EAAS,WAAW,CAAC,CAAA;AAG/B,EAAA,MAAM,gBAAA,GAAmBA,iBAAA,CAAY,CAAC,GAAA,KAAyB;AAC7D,IAAA,OAAO,KAAA,CAAM,QAAA,EAAU,QAAA,GAAW,GAAG,CAAA,IAAK,KAAA;AAAA,EAC5C,CAAA,EAAG,CAAC,KAAA,CAAM,QAAA,EAAU,QAAQ,CAAC,CAAA;AAG7B,EAAA,MAAM,QAAA,GAAWA,kBAAY,YAAoC;AAC/D,IAAA,IAAI;AACF,MAAA,MAAM,KAAA,GAAQ,MAAM,cAAA,CAAe,aAAa,CAAA;AAChD,MAAA,OAAO,KAAA,IAAS,IAAA;AAAA,IAClB,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF,CAAA,EAAG,CAAC,cAAA,EAAgB,aAAa,CAAC,CAAA;AAGlC,EAAAE,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,eAAA,IAAmB,CAAC,SAAA,EAAW;AAClC,MAAA,QAAA,CAAS,WAAS,EAAE,GAAG,IAAA,EAAM,SAAA,EAAW,OAAM,CAAE,CAAA;AAChD,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,WAAW,YAAY;AAC3B,MAAA,QAAA,CAAS,WAAS,EAAE,GAAG,IAAA,EAAM,SAAA,EAAW,MAAK,CAAE,CAAA;AAE/C,MAAA,IAAI;AACF,QAAA,MAAM,CAAC,OAAA,EAAS,QAAA,EAAU,YAAY,CAAA,GAAI,MAAM,QAAQ,GAAA,CAAI;AAAA,UAC1D,SAAqBD,qBAAA,CAAc,OAAO,CAAA,CAAE,KAAA,CAAM,MAAM,IAAI,CAAA;AAAA,UAC5D,SAA2BA,qBAAA,CAAc,QAAQ,CAAA,CAAE,KAAA,CAAM,MAAM,IAAI,CAAA;AAAA,UACnE,SAA0BA,qBAAA,CAAc,YAAY,CAAA,CAAE,KAAA,CAAM,MAAM,IAAI;AAAA,SACvE,CAAA;AAED,QAAA,QAAA,CAAS;AAAA,UACP,SAAA,EAAW,KAAA;AAAA,UACX,KAAA,EAAO,IAAA;AAAA,UACP,OAAA;AAAA,UACA,QAAA;AAAA,UACA;AAAA,SACD,CAAA;AAAA,MACH,SAAS,KAAA,EAAO;AACd,QAAA,QAAA,CAAS,CAAA,IAAA,MAAS;AAAA,UAChB,GAAG,IAAA;AAAA,UACH,SAAA,EAAW,KAAA;AAAA,UACX;AAAA,SACF,CAAE,CAAA;AAAA,MACJ;AAAA,IACF,CAAA;AAEA,IAAA,QAAA,EAAS;AAAA,EACX,CAAA,EAAG,CAAC,eAAA,EAAiB,SAAA,EAAW,QAAQ,CAAC,CAAA;AAGzC,EAAA,MAAM,YAAA,GAAqCF,cAAQ,OAAO;AAAA,IACxD,GAAG,KAAA;AAAA,IACH,cAAA;AAAA,IACA,eAAA;AAAA,IACA,mBAAA;AAAA,IACA,aAAA;AAAA,IACA,gBAAA;AAAA,IACA,iBAAA;AAAA,IACA,gBAAA;AAAA,IACA,cAAA,EAAgB;AAAA,GAClB,CAAA,EAAI;AAAA,IACF,KAAA;AAAA,IACA,cAAA;AAAA,IACA,eAAA;AAAA,IACA,mBAAA;AAAA,IACA,aAAA;AAAA,IACA,gBAAA;AAAA,IACA,iBAAA;AAAA,IACA,gBAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,sCACG,eAAA,CAAgB,QAAA,EAAhB,EAAyB,KAAA,EAAO,cAC9B,QAAA,EACH,CAAA;AAEJ;ACvNO,SAAS,WAAA,GAAoC;AAClD,EAAA,MAAM,OAAA,GAAUI,iBAAW,eAAe,CAAA;AAE1C,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,IAAI,MAAM,oDAAoD,CAAA;AAAA,EACtE;AAEA,EAAA,OAAO,OAAA;AACT;;;ACFO,SAAS,cAAA,GAAiB;AAC/B,EAAA,MAAM,EAAE,OAAA,EAAS,aAAA,EAAe,gBAAA,EAAkB,iBAAA,KAAsB,WAAA,EAAY;AAEpF,EAAA,OAAO;AAAA;AAAA,IAEL,WAAA,EAAa,OAAA,EAAS,WAAA,IAAe,EAAC;AAAA;AAAA,IAEtC,KAAA,EAAO,OAAA,EAAS,KAAA,IAAS,EAAC;AAAA;AAAA,IAE1B,aAAA;AAAA;AAAA,IAEA,gBAAA;AAAA;AAAA,IAEA;AAAA,GACF;AACF;;;ACrBO,SAAS,WAAA,GAAc;AAC5B,EAAA,MAAM,EAAE,QAAA,EAAU,gBAAA,EAAkB,eAAA,KAAoB,WAAA,EAAY;AAEpE,EAAA,OAAO;AAAA;AAAA,IAEL,QAAA,EAAU,QAAA,EAAU,QAAA,IAAY,EAAC;AAAA;AAAA,IAEjC,QAAQ,QAAA,EAAU,MAAA;AAAA;AAAA,IAElB,UAAU,QAAA,EAAU,QAAA;AAAA;AAAA,IAEpB,SAAA,EAAW,gBAAA;AAAA;AAAA,IAEX,OAAA,EAAS;AAAA,GACX;AACF;;;AC5BO,SAAS,eAAA,GAAkB;AAChC,EAAA,MAAM,EAAE,YAAA,EAAc,mBAAA,EAAoB,GAAI,WAAA,EAAY;AAE1D,EAAA,OAAO;AAAA;AAAA,IAEL,YAAA;AAAA;AAAA,IAEA,MAAM,YAAA,EAAc,IAAA;AAAA;AAAA,IAEpB,MAAA,EAAQ,YAAA,EAAc,MAAA,IAAU,EAAC;AAAA;AAAA,IAEjC,KAAA,EAAO,YAAA,EAAc,KAAA,IAAS,EAAC;AAAA;AAAA,IAE/B,QAAQ,YAAA,EAAc,MAAA;AAAA;AAAA,IAEtB,QAAA,EAAU,YAAA,EAAc,MAAA,KAAW,QAAA,IAAY,cAAc,MAAA,KAAW,UAAA;AAAA;AAAA,IAExE,UAAA,EAAY,cAAc,MAAA,KAAW,UAAA;AAAA;AAAA,IAErC,UAAA,EAAY,CAAC,GAAA,EAAa,SAAA,GAAY,CAAA,KAAe;AACnD,MAAA,MAAM,KAAA,GAAQ,YAAA,EAAc,MAAA,GAAS,GAAG,CAAA;AACxC,MAAA,MAAM,OAAA,GAAU,YAAA,EAAc,KAAA,GAAQ,GAAG,CAAA,IAAK,CAAA;AAC9C,MAAA,IAAI,KAAA,KAAU,QAAW,OAAO,IAAA;AAChC,MAAA,OAAO,UAAU,SAAA,IAAa,KAAA;AAAA,IAChC,CAAA;AAAA;AAAA,IAEA,OAAA,EAAS;AAAA,GACX;AACF;ACrBO,SAAS,kBAAkB,EAAE,UAAA,EAAY,QAAA,EAAU,QAAA,GAAW,MAAK,EAA2B;AACnG,EAAA,MAAM,EAAE,aAAA,EAAc,GAAI,cAAA,EAAe;AAEzC,EAAA,IAAI,CAAC,aAAA,CAAc,UAAU,CAAA,EAAG;AAC9B,IAAA,uBAAOC,cAAAA,CAAAC,mBAAA,EAAA,EAAG,QAAA,EAAA,QAAA,EAAS,CAAA;AAAA,EACrB;AAEA,EAAA,uBAAOD,cAAAA,CAAAC,mBAAA,EAAA,EAAG,QAAA,EAAS,CAAA;AACrB;ACRO,SAAS,eAAe,EAAE,IAAA,EAAM,QAAA,EAAU,QAAA,GAAW,MAAK,EAAwB;AACvF,EAAA,MAAM,EAAE,SAAA,EAAU,GAAI,WAAA,EAAY;AAElC,EAAA,IAAI,CAAC,SAAA,CAAU,IAAI,CAAA,EAAG;AACpB,IAAA,uBAAOD,cAAAA,CAAAC,mBAAAA,EAAA,EAAG,QAAA,EAAA,QAAA,EAAS,CAAA;AAAA,EACrB;AAEA,EAAA,uBAAOD,cAAAA,CAAAC,mBAAAA,EAAA,EAAG,QAAA,EAAS,CAAA;AACrB","file":"index.js","sourcesContent":["import { createContext } from 'react';\nimport type { SdkContext, SdkFeaturesState, SdkSubscription } from '@habeetat/sdk-core';\n\n/**\n * Habeetat SDK state\n */\nexport interface HabeetatState {\n /** Whether the SDK is loading initial data */\n isLoading: boolean;\n /** Error if any occurred */\n error: Error | null;\n /** SDK context (user, tenant, permissions) */\n context: SdkContext | null;\n /** Feature flags */\n features: SdkFeaturesState | null;\n /** Subscription info */\n subscription: SdkSubscription | null;\n}\n\n/**\n * Habeetat SDK context value\n */\nexport interface HabeetatContextValue extends HabeetatState {\n /** Refresh context from server */\n refreshContext: () => Promise<void>;\n /** Refresh features from server */\n refreshFeatures: () => Promise<void>;\n /** Refresh subscription from server */\n refreshSubscription: () => Promise<void>;\n /** Check if user has permission */\n hasPermission: (permission: string) => boolean;\n /** Check if user has any of the permissions */\n hasAnyPermission: (permissions: string[]) => boolean;\n /** Check if user has all permissions */\n hasAllPermissions: (permissions: string[]) => boolean;\n /** Check if feature is enabled */\n isFeatureEnabled: (key: string) => boolean;\n /** Get access token for API calls */\n getAccessToken: () => Promise<string | null>;\n}\n\n/**\n * Default context value\n */\nconst defaultContextValue: HabeetatContextValue = {\n isLoading: true,\n error: null,\n context: null,\n features: null,\n subscription: null,\n refreshContext: async () => {},\n refreshFeatures: async () => {},\n refreshSubscription: async () => {},\n hasPermission: () => false,\n hasAnyPermission: () => false,\n hasAllPermissions: () => false,\n isFeatureEnabled: () => false,\n getAccessToken: async () => null,\n};\n\n/**\n * Habeetat React Context\n */\nexport const HabeetatContext = createContext<HabeetatContextValue>(defaultContextValue);\n","import { useState, useEffect, useCallback, useMemo, type ReactNode } from 'react';\nimport { useLogto } from '@logto/react';\nimport { SDK_ENDPOINTS } from '@habeetat/sdk-core';\nimport type { SdkContext, SdkFeaturesState, SdkSubscription } from '@habeetat/sdk-core';\nimport { HabeetatContext, type HabeetatContextValue, type HabeetatState } from '../context/HabeetatContext';\n\n/**\n * Props for HabeetatProvider\n */\nexport interface HabeetatProviderProps {\n /** Platform API base URL (SDK endpoints) */\n platformUrl: string;\n /** Logto API resource identifier (for token requests) */\n logtoResource?: string;\n /** App ID for this application */\n appId?: string;\n /** Tenant slug (if known) */\n tenantSlug?: string;\n /** Whether to auto-fetch context on mount */\n autoFetch?: boolean;\n /** Children components */\n children: ReactNode;\n}\n\n/**\n * HabeetatProvider - Main provider for Habeetat SDK in React apps\n * \n * Must be used inside LogtoProvider from @logto/react\n * \n * @example\n * ```tsx\n * import { LogtoProvider } from '@logto/react';\n * import { HabeetatProvider } from '@habeetat/sdk-react';\n * \n * function App() {\n * return (\n * <LogtoProvider config={logtoConfig}>\n * <HabeetatProvider \n * platformUrl=\"https://api.habeetat.io\"\n * tenantSlug=\"acme-corp\"\n * >\n * <MyApp />\n * </HabeetatProvider>\n * </LogtoProvider>\n * );\n * }\n * ```\n */\nexport function HabeetatProvider({\n platformUrl,\n logtoResource,\n appId,\n tenantSlug,\n autoFetch = true,\n children,\n}: HabeetatProviderProps) {\n const { isAuthenticated, getAccessToken } = useLogto();\n \n // Use logtoResource if provided, otherwise fall back to platformUrl\n const tokenResource = logtoResource || platformUrl;\n \n const [state, setState] = useState<HabeetatState>({\n isLoading: true,\n error: null,\n context: null,\n features: null,\n subscription: null,\n });\n\n // Build API URL\n const apiUrl = useMemo(() => platformUrl.replace(/\\/$/, ''), [platformUrl]);\n\n // Fetch helper\n const fetchApi = useCallback(async <T,>(endpoint: string): Promise<T> => {\n // Use the Logto resource for token - this must match what's configured in Logto\n let token: string | undefined;\n try {\n token = await getAccessToken(tokenResource);\n console.log('[HabeetatSDK] Got token for resource:', tokenResource, token ? 'OK' : 'EMPTY');\n } catch (err) {\n console.error('[HabeetatSDK] Failed to get token for resource:', tokenResource, err);\n throw new Error(`Failed to get access token: ${err}`);\n }\n \n if (!token) {\n console.error('[HabeetatSDK] No token returned for resource:', tokenResource);\n throw new Error('No access token available');\n }\n\n const url = new URL(endpoint, apiUrl);\n if (tenantSlug) {\n url.searchParams.set('tenantSlug', tenantSlug);\n }\n if (appId) {\n url.searchParams.set('appId', appId);\n }\n\n const response = await fetch(url.toString(), {\n headers: {\n 'Authorization': `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n throw new Error(`API error: ${response.status}`);\n }\n\n return response.json() as Promise<T>;\n }, [apiUrl, tokenResource, tenantSlug, appId, getAccessToken]);\n\n // Refresh context\n const refreshContext = useCallback(async () => {\n try {\n const context = await fetchApi<SdkContext>(SDK_ENDPOINTS.CONTEXT);\n setState(prev => ({ ...prev, context, error: null }));\n } catch (error) {\n setState(prev => ({ ...prev, error: error as Error }));\n }\n }, [fetchApi]);\n\n // Refresh features\n const refreshFeatures = useCallback(async () => {\n try {\n const features = await fetchApi<SdkFeaturesState>(SDK_ENDPOINTS.FEATURES);\n setState(prev => ({ ...prev, features, error: null }));\n } catch (error) {\n setState(prev => ({ ...prev, error: error as Error }));\n }\n }, [fetchApi]);\n\n // Refresh subscription\n const refreshSubscription = useCallback(async () => {\n try {\n const subscription = await fetchApi<SdkSubscription>(SDK_ENDPOINTS.SUBSCRIPTION);\n setState(prev => ({ ...prev, subscription, error: null }));\n } catch (error) {\n setState(prev => ({ ...prev, error: error as Error }));\n }\n }, [fetchApi]);\n\n // Permission helpers\n const hasPermission = useCallback((permission: string): boolean => {\n return state.context?.permissions?.includes(permission) ?? false;\n }, [state.context?.permissions]);\n\n const hasAnyPermission = useCallback((permissions: string[]): boolean => {\n return permissions.some(p => state.context?.permissions?.includes(p));\n }, [state.context?.permissions]);\n\n const hasAllPermissions = useCallback((permissions: string[]): boolean => {\n return permissions.every(p => state.context?.permissions?.includes(p));\n }, [state.context?.permissions]);\n\n // Feature helper\n const isFeatureEnabled = useCallback((key: string): boolean => {\n return state.features?.features?.[key] ?? false;\n }, [state.features?.features]);\n\n // Get access token helper\n const getToken = useCallback(async (): Promise<string | null> => {\n try {\n const token = await getAccessToken(tokenResource);\n return token ?? null;\n } catch {\n return null;\n }\n }, [getAccessToken, tokenResource]);\n\n // Auto-fetch on mount when authenticated\n useEffect(() => {\n if (!isAuthenticated || !autoFetch) {\n setState(prev => ({ ...prev, isLoading: false }));\n return;\n }\n\n const fetchAll = async () => {\n setState(prev => ({ ...prev, isLoading: true }));\n \n try {\n const [context, features, subscription] = await Promise.all([\n fetchApi<SdkContext>(SDK_ENDPOINTS.CONTEXT).catch(() => null),\n fetchApi<SdkFeaturesState>(SDK_ENDPOINTS.FEATURES).catch(() => null),\n fetchApi<SdkSubscription>(SDK_ENDPOINTS.SUBSCRIPTION).catch(() => null),\n ]);\n\n setState({\n isLoading: false,\n error: null,\n context,\n features,\n subscription,\n });\n } catch (error) {\n setState(prev => ({\n ...prev,\n isLoading: false,\n error: error as Error,\n }));\n }\n };\n\n fetchAll();\n }, [isAuthenticated, autoFetch, fetchApi]);\n\n // Build context value\n const contextValue: HabeetatContextValue = useMemo(() => ({\n ...state,\n refreshContext,\n refreshFeatures,\n refreshSubscription,\n hasPermission,\n hasAnyPermission,\n hasAllPermissions,\n isFeatureEnabled,\n getAccessToken: getToken,\n }), [\n state,\n refreshContext,\n refreshFeatures,\n refreshSubscription,\n hasPermission,\n hasAnyPermission,\n hasAllPermissions,\n isFeatureEnabled,\n getToken,\n ]);\n\n return (\n <HabeetatContext.Provider value={contextValue}>\n {children}\n </HabeetatContext.Provider>\n );\n}\n","import { useContext } from 'react';\nimport { HabeetatContext, type HabeetatContextValue } from '../context/HabeetatContext';\n\n/**\n * Hook to access Habeetat SDK context\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { context, isLoading, error } = useHabeetat();\n * \n * if (isLoading) return <Spinner />;\n * if (error) return <Error message={error.message} />;\n * \n * return <div>Hello, {context?.user.name}</div>;\n * }\n * ```\n */\nexport function useHabeetat(): HabeetatContextValue {\n const context = useContext(HabeetatContext);\n \n if (!context) {\n throw new Error('useHabeetat must be used within a HabeetatProvider');\n }\n \n return context;\n}\n","import { useHabeetat } from './useHabeetat';\n\n/**\n * Hook for permission checks\n * \n * @example\n * ```tsx\n * function ContactsPage() {\n * const { hasPermission, hasAnyPermission } = usePermissions();\n * \n * const canRead = hasPermission('contacts:read');\n * const canWrite = hasPermission('contacts:write');\n * const canManage = hasAnyPermission(['contacts:delete', 'contacts:admin']);\n * \n * return (\n * <div>\n * {canRead && <ContactsList />}\n * {canWrite && <AddContactButton />}\n * {canManage && <ManageContactsButton />}\n * </div>\n * );\n * }\n * ```\n */\nexport function usePermissions() {\n const { context, hasPermission, hasAnyPermission, hasAllPermissions } = useHabeetat();\n \n return {\n /** All user permissions */\n permissions: context?.permissions ?? [],\n /** All user roles */\n roles: context?.roles ?? [],\n /** Check if user has a specific permission */\n hasPermission,\n /** Check if user has any of the specified permissions */\n hasAnyPermission,\n /** Check if user has all of the specified permissions */\n hasAllPermissions,\n };\n}\n","import { useHabeetat } from './useHabeetat';\n\n/**\n * Hook for feature flag checks\n * \n * @example\n * ```tsx\n * function DealsPage() {\n * const { isEnabled, features } = useFeatures();\n * \n * if (!isEnabled('crm.deals.enabled')) {\n * return <UpgradePrompt feature=\"Deals\" />;\n * }\n * \n * return <DealsList />;\n * }\n * ```\n */\nexport function useFeatures() {\n const { features, isFeatureEnabled, refreshFeatures } = useHabeetat();\n \n return {\n /** All feature flags */\n features: features?.features ?? {},\n /** Feature source (plan, tenant, etc.) */\n source: features?.source,\n /** Plan code if source is plan */\n planCode: features?.planCode,\n /** Check if a feature is enabled */\n isEnabled: isFeatureEnabled,\n /** Refresh features from server */\n refresh: refreshFeatures,\n };\n}\n","import { useHabeetat } from './useHabeetat';\n\n/**\n * Hook for subscription and plan info\n */\nexport function useSubscription() {\n const { subscription, refreshSubscription } = useHabeetat();\n \n return {\n /** Current subscription */\n subscription,\n /** Current plan */\n plan: subscription?.plan,\n /** Plan limits */\n limits: subscription?.limits ?? {},\n /** Current usage */\n usage: subscription?.usage ?? {},\n /** Subscription status */\n status: subscription?.status,\n /** Check if subscription is active */\n isActive: subscription?.status === 'active' || subscription?.status === 'trialing',\n /** Check if in trial */\n isTrialing: subscription?.status === 'trialing',\n /** Check limit */\n checkLimit: (key: string, increment = 0): boolean => {\n const limit = subscription?.limits?.[key];\n const current = subscription?.usage?.[key] ?? 0;\n if (limit === undefined) return true;\n return current + increment <= limit;\n },\n /** Refresh subscription */\n refresh: refreshSubscription,\n };\n}\n","import { type ReactNode } from 'react';\nimport { usePermissions } from '../hooks/usePermissions';\n\ninterface RequirePermissionProps {\n permission: string;\n children: ReactNode;\n fallback?: ReactNode;\n}\n\n/**\n * Component that renders children only if user has the required permission\n */\nexport function RequirePermission({ permission, children, fallback = null }: RequirePermissionProps) {\n const { hasPermission } = usePermissions();\n \n if (!hasPermission(permission)) {\n return <>{fallback}</>;\n }\n \n return <>{children}</>;\n}\n","import { type ReactNode } from 'react';\nimport { useFeatures } from '../hooks/useFeatures';\n\ninterface RequireFeatureProps {\n flag: string;\n children: ReactNode;\n fallback?: ReactNode;\n}\n\n/**\n * Component that renders children only if the feature flag is enabled\n */\nexport function RequireFeature({ flag, children, fallback = null }: RequireFeatureProps) {\n const { isEnabled } = useFeatures();\n \n if (!isEnabled(flag)) {\n return <>{fallback}</>;\n }\n \n return <>{children}</>;\n}\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,260 @@
1
+ import { createContext, useState, useMemo, useCallback, useEffect, useContext } from 'react';
2
+ import { useLogto } from '@logto/react';
3
+ import { SDK_ENDPOINTS } from '@habeetat/sdk-core';
4
+ import { jsx, Fragment } from 'react/jsx-runtime';
5
+
6
+ // src/provider/HabeetatProvider.tsx
7
+ var defaultContextValue = {
8
+ isLoading: true,
9
+ error: null,
10
+ context: null,
11
+ features: null,
12
+ subscription: null,
13
+ refreshContext: async () => {
14
+ },
15
+ refreshFeatures: async () => {
16
+ },
17
+ refreshSubscription: async () => {
18
+ },
19
+ hasPermission: () => false,
20
+ hasAnyPermission: () => false,
21
+ hasAllPermissions: () => false,
22
+ isFeatureEnabled: () => false,
23
+ getAccessToken: async () => null
24
+ };
25
+ var HabeetatContext = createContext(defaultContextValue);
26
+ function HabeetatProvider({
27
+ platformUrl,
28
+ logtoResource,
29
+ appId,
30
+ tenantSlug,
31
+ autoFetch = true,
32
+ children
33
+ }) {
34
+ const { isAuthenticated, getAccessToken } = useLogto();
35
+ const tokenResource = logtoResource || platformUrl;
36
+ const [state, setState] = useState({
37
+ isLoading: true,
38
+ error: null,
39
+ context: null,
40
+ features: null,
41
+ subscription: null
42
+ });
43
+ const apiUrl = useMemo(() => platformUrl.replace(/\/$/, ""), [platformUrl]);
44
+ const fetchApi = useCallback(async (endpoint) => {
45
+ let token;
46
+ try {
47
+ token = await getAccessToken(tokenResource);
48
+ console.log("[HabeetatSDK] Got token for resource:", tokenResource, token ? "OK" : "EMPTY");
49
+ } catch (err) {
50
+ console.error("[HabeetatSDK] Failed to get token for resource:", tokenResource, err);
51
+ throw new Error(`Failed to get access token: ${err}`);
52
+ }
53
+ if (!token) {
54
+ console.error("[HabeetatSDK] No token returned for resource:", tokenResource);
55
+ throw new Error("No access token available");
56
+ }
57
+ const url = new URL(endpoint, apiUrl);
58
+ if (tenantSlug) {
59
+ url.searchParams.set("tenantSlug", tenantSlug);
60
+ }
61
+ if (appId) {
62
+ url.searchParams.set("appId", appId);
63
+ }
64
+ const response = await fetch(url.toString(), {
65
+ headers: {
66
+ "Authorization": `Bearer ${token}`,
67
+ "Content-Type": "application/json"
68
+ }
69
+ });
70
+ if (!response.ok) {
71
+ throw new Error(`API error: ${response.status}`);
72
+ }
73
+ return response.json();
74
+ }, [apiUrl, tokenResource, tenantSlug, appId, getAccessToken]);
75
+ const refreshContext = useCallback(async () => {
76
+ try {
77
+ const context = await fetchApi(SDK_ENDPOINTS.CONTEXT);
78
+ setState((prev) => ({ ...prev, context, error: null }));
79
+ } catch (error) {
80
+ setState((prev) => ({ ...prev, error }));
81
+ }
82
+ }, [fetchApi]);
83
+ const refreshFeatures = useCallback(async () => {
84
+ try {
85
+ const features = await fetchApi(SDK_ENDPOINTS.FEATURES);
86
+ setState((prev) => ({ ...prev, features, error: null }));
87
+ } catch (error) {
88
+ setState((prev) => ({ ...prev, error }));
89
+ }
90
+ }, [fetchApi]);
91
+ const refreshSubscription = useCallback(async () => {
92
+ try {
93
+ const subscription = await fetchApi(SDK_ENDPOINTS.SUBSCRIPTION);
94
+ setState((prev) => ({ ...prev, subscription, error: null }));
95
+ } catch (error) {
96
+ setState((prev) => ({ ...prev, error }));
97
+ }
98
+ }, [fetchApi]);
99
+ const hasPermission = useCallback((permission) => {
100
+ return state.context?.permissions?.includes(permission) ?? false;
101
+ }, [state.context?.permissions]);
102
+ const hasAnyPermission = useCallback((permissions) => {
103
+ return permissions.some((p) => state.context?.permissions?.includes(p));
104
+ }, [state.context?.permissions]);
105
+ const hasAllPermissions = useCallback((permissions) => {
106
+ return permissions.every((p) => state.context?.permissions?.includes(p));
107
+ }, [state.context?.permissions]);
108
+ const isFeatureEnabled = useCallback((key) => {
109
+ return state.features?.features?.[key] ?? false;
110
+ }, [state.features?.features]);
111
+ const getToken = useCallback(async () => {
112
+ try {
113
+ const token = await getAccessToken(tokenResource);
114
+ return token ?? null;
115
+ } catch {
116
+ return null;
117
+ }
118
+ }, [getAccessToken, tokenResource]);
119
+ useEffect(() => {
120
+ if (!isAuthenticated || !autoFetch) {
121
+ setState((prev) => ({ ...prev, isLoading: false }));
122
+ return;
123
+ }
124
+ const fetchAll = async () => {
125
+ setState((prev) => ({ ...prev, isLoading: true }));
126
+ try {
127
+ const [context, features, subscription] = await Promise.all([
128
+ fetchApi(SDK_ENDPOINTS.CONTEXT).catch(() => null),
129
+ fetchApi(SDK_ENDPOINTS.FEATURES).catch(() => null),
130
+ fetchApi(SDK_ENDPOINTS.SUBSCRIPTION).catch(() => null)
131
+ ]);
132
+ setState({
133
+ isLoading: false,
134
+ error: null,
135
+ context,
136
+ features,
137
+ subscription
138
+ });
139
+ } catch (error) {
140
+ setState((prev) => ({
141
+ ...prev,
142
+ isLoading: false,
143
+ error
144
+ }));
145
+ }
146
+ };
147
+ fetchAll();
148
+ }, [isAuthenticated, autoFetch, fetchApi]);
149
+ const contextValue = useMemo(() => ({
150
+ ...state,
151
+ refreshContext,
152
+ refreshFeatures,
153
+ refreshSubscription,
154
+ hasPermission,
155
+ hasAnyPermission,
156
+ hasAllPermissions,
157
+ isFeatureEnabled,
158
+ getAccessToken: getToken
159
+ }), [
160
+ state,
161
+ refreshContext,
162
+ refreshFeatures,
163
+ refreshSubscription,
164
+ hasPermission,
165
+ hasAnyPermission,
166
+ hasAllPermissions,
167
+ isFeatureEnabled,
168
+ getToken
169
+ ]);
170
+ return /* @__PURE__ */ jsx(HabeetatContext.Provider, { value: contextValue, children });
171
+ }
172
+ function useHabeetat() {
173
+ const context = useContext(HabeetatContext);
174
+ if (!context) {
175
+ throw new Error("useHabeetat must be used within a HabeetatProvider");
176
+ }
177
+ return context;
178
+ }
179
+
180
+ // src/hooks/usePermissions.ts
181
+ function usePermissions() {
182
+ const { context, hasPermission, hasAnyPermission, hasAllPermissions } = useHabeetat();
183
+ return {
184
+ /** All user permissions */
185
+ permissions: context?.permissions ?? [],
186
+ /** All user roles */
187
+ roles: context?.roles ?? [],
188
+ /** Check if user has a specific permission */
189
+ hasPermission,
190
+ /** Check if user has any of the specified permissions */
191
+ hasAnyPermission,
192
+ /** Check if user has all of the specified permissions */
193
+ hasAllPermissions
194
+ };
195
+ }
196
+
197
+ // src/hooks/useFeatures.ts
198
+ function useFeatures() {
199
+ const { features, isFeatureEnabled, refreshFeatures } = useHabeetat();
200
+ return {
201
+ /** All feature flags */
202
+ features: features?.features ?? {},
203
+ /** Feature source (plan, tenant, etc.) */
204
+ source: features?.source,
205
+ /** Plan code if source is plan */
206
+ planCode: features?.planCode,
207
+ /** Check if a feature is enabled */
208
+ isEnabled: isFeatureEnabled,
209
+ /** Refresh features from server */
210
+ refresh: refreshFeatures
211
+ };
212
+ }
213
+
214
+ // src/hooks/useSubscription.ts
215
+ function useSubscription() {
216
+ const { subscription, refreshSubscription } = useHabeetat();
217
+ return {
218
+ /** Current subscription */
219
+ subscription,
220
+ /** Current plan */
221
+ plan: subscription?.plan,
222
+ /** Plan limits */
223
+ limits: subscription?.limits ?? {},
224
+ /** Current usage */
225
+ usage: subscription?.usage ?? {},
226
+ /** Subscription status */
227
+ status: subscription?.status,
228
+ /** Check if subscription is active */
229
+ isActive: subscription?.status === "active" || subscription?.status === "trialing",
230
+ /** Check if in trial */
231
+ isTrialing: subscription?.status === "trialing",
232
+ /** Check limit */
233
+ checkLimit: (key, increment = 0) => {
234
+ const limit = subscription?.limits?.[key];
235
+ const current = subscription?.usage?.[key] ?? 0;
236
+ if (limit === void 0) return true;
237
+ return current + increment <= limit;
238
+ },
239
+ /** Refresh subscription */
240
+ refresh: refreshSubscription
241
+ };
242
+ }
243
+ function RequirePermission({ permission, children, fallback = null }) {
244
+ const { hasPermission } = usePermissions();
245
+ if (!hasPermission(permission)) {
246
+ return /* @__PURE__ */ jsx(Fragment, { children: fallback });
247
+ }
248
+ return /* @__PURE__ */ jsx(Fragment, { children });
249
+ }
250
+ function RequireFeature({ flag, children, fallback = null }) {
251
+ const { isEnabled } = useFeatures();
252
+ if (!isEnabled(flag)) {
253
+ return /* @__PURE__ */ jsx(Fragment, { children: fallback });
254
+ }
255
+ return /* @__PURE__ */ jsx(Fragment, { children });
256
+ }
257
+
258
+ export { HabeetatContext, HabeetatProvider, RequireFeature, RequirePermission, useFeatures, useHabeetat, usePermissions, useSubscription };
259
+ //# sourceMappingURL=index.mjs.map
260
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/context/HabeetatContext.ts","../src/provider/HabeetatProvider.tsx","../src/hooks/useHabeetat.ts","../src/hooks/usePermissions.ts","../src/hooks/useFeatures.ts","../src/hooks/useSubscription.ts","../src/components/RequirePermission.tsx","../src/components/RequireFeature.tsx"],"names":["jsx","Fragment"],"mappings":"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","file":"index.mjs","sourcesContent":["import { createContext } from 'react';\nimport type { SdkContext, SdkFeaturesState, SdkSubscription } from '@habeetat/sdk-core';\n\n/**\n * Habeetat SDK state\n */\nexport interface HabeetatState {\n /** Whether the SDK is loading initial data */\n isLoading: boolean;\n /** Error if any occurred */\n error: Error | null;\n /** SDK context (user, tenant, permissions) */\n context: SdkContext | null;\n /** Feature flags */\n features: SdkFeaturesState | null;\n /** Subscription info */\n subscription: SdkSubscription | null;\n}\n\n/**\n * Habeetat SDK context value\n */\nexport interface HabeetatContextValue extends HabeetatState {\n /** Refresh context from server */\n refreshContext: () => Promise<void>;\n /** Refresh features from server */\n refreshFeatures: () => Promise<void>;\n /** Refresh subscription from server */\n refreshSubscription: () => Promise<void>;\n /** Check if user has permission */\n hasPermission: (permission: string) => boolean;\n /** Check if user has any of the permissions */\n hasAnyPermission: (permissions: string[]) => boolean;\n /** Check if user has all permissions */\n hasAllPermissions: (permissions: string[]) => boolean;\n /** Check if feature is enabled */\n isFeatureEnabled: (key: string) => boolean;\n /** Get access token for API calls */\n getAccessToken: () => Promise<string | null>;\n}\n\n/**\n * Default context value\n */\nconst defaultContextValue: HabeetatContextValue = {\n isLoading: true,\n error: null,\n context: null,\n features: null,\n subscription: null,\n refreshContext: async () => {},\n refreshFeatures: async () => {},\n refreshSubscription: async () => {},\n hasPermission: () => false,\n hasAnyPermission: () => false,\n hasAllPermissions: () => false,\n isFeatureEnabled: () => false,\n getAccessToken: async () => null,\n};\n\n/**\n * Habeetat React Context\n */\nexport const HabeetatContext = createContext<HabeetatContextValue>(defaultContextValue);\n","import { useState, useEffect, useCallback, useMemo, type ReactNode } from 'react';\nimport { useLogto } from '@logto/react';\nimport { SDK_ENDPOINTS } from '@habeetat/sdk-core';\nimport type { SdkContext, SdkFeaturesState, SdkSubscription } from '@habeetat/sdk-core';\nimport { HabeetatContext, type HabeetatContextValue, type HabeetatState } from '../context/HabeetatContext';\n\n/**\n * Props for HabeetatProvider\n */\nexport interface HabeetatProviderProps {\n /** Platform API base URL (SDK endpoints) */\n platformUrl: string;\n /** Logto API resource identifier (for token requests) */\n logtoResource?: string;\n /** App ID for this application */\n appId?: string;\n /** Tenant slug (if known) */\n tenantSlug?: string;\n /** Whether to auto-fetch context on mount */\n autoFetch?: boolean;\n /** Children components */\n children: ReactNode;\n}\n\n/**\n * HabeetatProvider - Main provider for Habeetat SDK in React apps\n * \n * Must be used inside LogtoProvider from @logto/react\n * \n * @example\n * ```tsx\n * import { LogtoProvider } from '@logto/react';\n * import { HabeetatProvider } from '@habeetat/sdk-react';\n * \n * function App() {\n * return (\n * <LogtoProvider config={logtoConfig}>\n * <HabeetatProvider \n * platformUrl=\"https://api.habeetat.io\"\n * tenantSlug=\"acme-corp\"\n * >\n * <MyApp />\n * </HabeetatProvider>\n * </LogtoProvider>\n * );\n * }\n * ```\n */\nexport function HabeetatProvider({\n platformUrl,\n logtoResource,\n appId,\n tenantSlug,\n autoFetch = true,\n children,\n}: HabeetatProviderProps) {\n const { isAuthenticated, getAccessToken } = useLogto();\n \n // Use logtoResource if provided, otherwise fall back to platformUrl\n const tokenResource = logtoResource || platformUrl;\n \n const [state, setState] = useState<HabeetatState>({\n isLoading: true,\n error: null,\n context: null,\n features: null,\n subscription: null,\n });\n\n // Build API URL\n const apiUrl = useMemo(() => platformUrl.replace(/\\/$/, ''), [platformUrl]);\n\n // Fetch helper\n const fetchApi = useCallback(async <T,>(endpoint: string): Promise<T> => {\n // Use the Logto resource for token - this must match what's configured in Logto\n let token: string | undefined;\n try {\n token = await getAccessToken(tokenResource);\n console.log('[HabeetatSDK] Got token for resource:', tokenResource, token ? 'OK' : 'EMPTY');\n } catch (err) {\n console.error('[HabeetatSDK] Failed to get token for resource:', tokenResource, err);\n throw new Error(`Failed to get access token: ${err}`);\n }\n \n if (!token) {\n console.error('[HabeetatSDK] No token returned for resource:', tokenResource);\n throw new Error('No access token available');\n }\n\n const url = new URL(endpoint, apiUrl);\n if (tenantSlug) {\n url.searchParams.set('tenantSlug', tenantSlug);\n }\n if (appId) {\n url.searchParams.set('appId', appId);\n }\n\n const response = await fetch(url.toString(), {\n headers: {\n 'Authorization': `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n throw new Error(`API error: ${response.status}`);\n }\n\n return response.json() as Promise<T>;\n }, [apiUrl, tokenResource, tenantSlug, appId, getAccessToken]);\n\n // Refresh context\n const refreshContext = useCallback(async () => {\n try {\n const context = await fetchApi<SdkContext>(SDK_ENDPOINTS.CONTEXT);\n setState(prev => ({ ...prev, context, error: null }));\n } catch (error) {\n setState(prev => ({ ...prev, error: error as Error }));\n }\n }, [fetchApi]);\n\n // Refresh features\n const refreshFeatures = useCallback(async () => {\n try {\n const features = await fetchApi<SdkFeaturesState>(SDK_ENDPOINTS.FEATURES);\n setState(prev => ({ ...prev, features, error: null }));\n } catch (error) {\n setState(prev => ({ ...prev, error: error as Error }));\n }\n }, [fetchApi]);\n\n // Refresh subscription\n const refreshSubscription = useCallback(async () => {\n try {\n const subscription = await fetchApi<SdkSubscription>(SDK_ENDPOINTS.SUBSCRIPTION);\n setState(prev => ({ ...prev, subscription, error: null }));\n } catch (error) {\n setState(prev => ({ ...prev, error: error as Error }));\n }\n }, [fetchApi]);\n\n // Permission helpers\n const hasPermission = useCallback((permission: string): boolean => {\n return state.context?.permissions?.includes(permission) ?? false;\n }, [state.context?.permissions]);\n\n const hasAnyPermission = useCallback((permissions: string[]): boolean => {\n return permissions.some(p => state.context?.permissions?.includes(p));\n }, [state.context?.permissions]);\n\n const hasAllPermissions = useCallback((permissions: string[]): boolean => {\n return permissions.every(p => state.context?.permissions?.includes(p));\n }, [state.context?.permissions]);\n\n // Feature helper\n const isFeatureEnabled = useCallback((key: string): boolean => {\n return state.features?.features?.[key] ?? false;\n }, [state.features?.features]);\n\n // Get access token helper\n const getToken = useCallback(async (): Promise<string | null> => {\n try {\n const token = await getAccessToken(tokenResource);\n return token ?? null;\n } catch {\n return null;\n }\n }, [getAccessToken, tokenResource]);\n\n // Auto-fetch on mount when authenticated\n useEffect(() => {\n if (!isAuthenticated || !autoFetch) {\n setState(prev => ({ ...prev, isLoading: false }));\n return;\n }\n\n const fetchAll = async () => {\n setState(prev => ({ ...prev, isLoading: true }));\n \n try {\n const [context, features, subscription] = await Promise.all([\n fetchApi<SdkContext>(SDK_ENDPOINTS.CONTEXT).catch(() => null),\n fetchApi<SdkFeaturesState>(SDK_ENDPOINTS.FEATURES).catch(() => null),\n fetchApi<SdkSubscription>(SDK_ENDPOINTS.SUBSCRIPTION).catch(() => null),\n ]);\n\n setState({\n isLoading: false,\n error: null,\n context,\n features,\n subscription,\n });\n } catch (error) {\n setState(prev => ({\n ...prev,\n isLoading: false,\n error: error as Error,\n }));\n }\n };\n\n fetchAll();\n }, [isAuthenticated, autoFetch, fetchApi]);\n\n // Build context value\n const contextValue: HabeetatContextValue = useMemo(() => ({\n ...state,\n refreshContext,\n refreshFeatures,\n refreshSubscription,\n hasPermission,\n hasAnyPermission,\n hasAllPermissions,\n isFeatureEnabled,\n getAccessToken: getToken,\n }), [\n state,\n refreshContext,\n refreshFeatures,\n refreshSubscription,\n hasPermission,\n hasAnyPermission,\n hasAllPermissions,\n isFeatureEnabled,\n getToken,\n ]);\n\n return (\n <HabeetatContext.Provider value={contextValue}>\n {children}\n </HabeetatContext.Provider>\n );\n}\n","import { useContext } from 'react';\nimport { HabeetatContext, type HabeetatContextValue } from '../context/HabeetatContext';\n\n/**\n * Hook to access Habeetat SDK context\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { context, isLoading, error } = useHabeetat();\n * \n * if (isLoading) return <Spinner />;\n * if (error) return <Error message={error.message} />;\n * \n * return <div>Hello, {context?.user.name}</div>;\n * }\n * ```\n */\nexport function useHabeetat(): HabeetatContextValue {\n const context = useContext(HabeetatContext);\n \n if (!context) {\n throw new Error('useHabeetat must be used within a HabeetatProvider');\n }\n \n return context;\n}\n","import { useHabeetat } from './useHabeetat';\n\n/**\n * Hook for permission checks\n * \n * @example\n * ```tsx\n * function ContactsPage() {\n * const { hasPermission, hasAnyPermission } = usePermissions();\n * \n * const canRead = hasPermission('contacts:read');\n * const canWrite = hasPermission('contacts:write');\n * const canManage = hasAnyPermission(['contacts:delete', 'contacts:admin']);\n * \n * return (\n * <div>\n * {canRead && <ContactsList />}\n * {canWrite && <AddContactButton />}\n * {canManage && <ManageContactsButton />}\n * </div>\n * );\n * }\n * ```\n */\nexport function usePermissions() {\n const { context, hasPermission, hasAnyPermission, hasAllPermissions } = useHabeetat();\n \n return {\n /** All user permissions */\n permissions: context?.permissions ?? [],\n /** All user roles */\n roles: context?.roles ?? [],\n /** Check if user has a specific permission */\n hasPermission,\n /** Check if user has any of the specified permissions */\n hasAnyPermission,\n /** Check if user has all of the specified permissions */\n hasAllPermissions,\n };\n}\n","import { useHabeetat } from './useHabeetat';\n\n/**\n * Hook for feature flag checks\n * \n * @example\n * ```tsx\n * function DealsPage() {\n * const { isEnabled, features } = useFeatures();\n * \n * if (!isEnabled('crm.deals.enabled')) {\n * return <UpgradePrompt feature=\"Deals\" />;\n * }\n * \n * return <DealsList />;\n * }\n * ```\n */\nexport function useFeatures() {\n const { features, isFeatureEnabled, refreshFeatures } = useHabeetat();\n \n return {\n /** All feature flags */\n features: features?.features ?? {},\n /** Feature source (plan, tenant, etc.) */\n source: features?.source,\n /** Plan code if source is plan */\n planCode: features?.planCode,\n /** Check if a feature is enabled */\n isEnabled: isFeatureEnabled,\n /** Refresh features from server */\n refresh: refreshFeatures,\n };\n}\n","import { useHabeetat } from './useHabeetat';\n\n/**\n * Hook for subscription and plan info\n */\nexport function useSubscription() {\n const { subscription, refreshSubscription } = useHabeetat();\n \n return {\n /** Current subscription */\n subscription,\n /** Current plan */\n plan: subscription?.plan,\n /** Plan limits */\n limits: subscription?.limits ?? {},\n /** Current usage */\n usage: subscription?.usage ?? {},\n /** Subscription status */\n status: subscription?.status,\n /** Check if subscription is active */\n isActive: subscription?.status === 'active' || subscription?.status === 'trialing',\n /** Check if in trial */\n isTrialing: subscription?.status === 'trialing',\n /** Check limit */\n checkLimit: (key: string, increment = 0): boolean => {\n const limit = subscription?.limits?.[key];\n const current = subscription?.usage?.[key] ?? 0;\n if (limit === undefined) return true;\n return current + increment <= limit;\n },\n /** Refresh subscription */\n refresh: refreshSubscription,\n };\n}\n","import { type ReactNode } from 'react';\nimport { usePermissions } from '../hooks/usePermissions';\n\ninterface RequirePermissionProps {\n permission: string;\n children: ReactNode;\n fallback?: ReactNode;\n}\n\n/**\n * Component that renders children only if user has the required permission\n */\nexport function RequirePermission({ permission, children, fallback = null }: RequirePermissionProps) {\n const { hasPermission } = usePermissions();\n \n if (!hasPermission(permission)) {\n return <>{fallback}</>;\n }\n \n return <>{children}</>;\n}\n","import { type ReactNode } from 'react';\nimport { useFeatures } from '../hooks/useFeatures';\n\ninterface RequireFeatureProps {\n flag: string;\n children: ReactNode;\n fallback?: ReactNode;\n}\n\n/**\n * Component that renders children only if the feature flag is enabled\n */\nexport function RequireFeature({ flag, children, fallback = null }: RequireFeatureProps) {\n const { isEnabled } = useFeatures();\n \n if (!isEnabled(flag)) {\n return <>{fallback}</>;\n }\n \n return <>{children}</>;\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@habeetat/sdk-react",
3
+ "version": "0.1.0-dev.20260323155801.0ae6298",
4
+ "description": "Habeetat Platform SDK for React applications",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "dev": "tsup --watch",
21
+ "typecheck": "tsc --noEmit",
22
+ "clean": "rm -rf dist"
23
+ },
24
+ "dependencies": {
25
+ "@habeetat/sdk-core": "0.1.0-dev.20260323155801.0ae6298"
26
+ },
27
+ "peerDependencies": {
28
+ "@logto/react": ">=3.0.0",
29
+ "react": ">=18.0.0"
30
+ },
31
+ "devDependencies": {
32
+ "@types/react": "^18.2.0",
33
+ "react": "^18.2.0",
34
+ "tsup": "^8.0.0",
35
+ "typescript": "^5.3.0"
36
+ },
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://bitbucket.org/habeetat/nhp.git",
43
+ "directory": "packages/sdk-frontend"
44
+ },
45
+ "license": "MIT",
46
+ "keywords": [
47
+ "habeetat",
48
+ "sdk",
49
+ "react",
50
+ "frontend",
51
+ "platform"
52
+ ]
53
+ }