@ram_28/kf-ai-sdk 1.0.16 → 1.0.18

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.
@@ -0,0 +1,402 @@
1
+ # useAuth
2
+
3
+ ## Brief Description
4
+
5
+ - Provides authentication state management with support for multiple OAuth providers (Google, Microsoft, GitHub, custom)
6
+ - Handles session checking, login flow initiation, and logout operations with automatic session refresh
7
+ - Includes role-based access control helpers (`hasRole`, `hasAnyRole`) for permission checking
8
+ - Must be used within an `AuthProvider` component that manages the authentication context
9
+
10
+ ## Type Reference
11
+
12
+ ```typescript
13
+ import { useAuth, AuthProvider } from "@ram_28/kf-ai-sdk/auth";
14
+ import type {
15
+ UseAuthReturnType,
16
+ UserDetailsType,
17
+ AuthStatusType,
18
+ AuthProviderPropsType,
19
+ AuthProviderNameType,
20
+ AuthConfigType,
21
+ AuthEndpointConfigType,
22
+ LoginOptionsType,
23
+ LogoutOptionsType,
24
+ SessionResponseType,
25
+ } from "@ram_28/kf-ai-sdk/auth/types";
26
+
27
+ // User details from session
28
+ interface UserDetailsType {
29
+ _id: string;
30
+ _name: string;
31
+ Role: string;
32
+ [key: string]: unknown;
33
+ }
34
+
35
+ // Session response from API
36
+ interface SessionResponseType {
37
+ userDetails: UserDetailsType;
38
+ staticBaseUrl: string;
39
+ buildId: string;
40
+ }
41
+
42
+ // Authentication status
43
+ type AuthStatusType = "loading" | "authenticated" | "unauthenticated";
44
+
45
+ // Supported auth providers
46
+ type AuthProviderNameType = "google" | "microsoft" | "github" | "custom";
47
+
48
+ // Auth endpoint configuration for a provider
49
+ interface AuthEndpointConfigType {
50
+ loginPath: string;
51
+ logoutPath?: string;
52
+ callbackPath?: string;
53
+ }
54
+
55
+ // Global auth configuration
56
+ interface AuthConfigType {
57
+ baseUrl?: string;
58
+ sessionEndpoint: string;
59
+ providers: Partial<Record<AuthProviderNameType, AuthEndpointConfigType>>;
60
+ defaultProvider: AuthProviderNameType;
61
+ autoRedirect: boolean;
62
+ loginRedirectUrl?: string;
63
+ callbackUrl?: string;
64
+ sessionCheckInterval: number;
65
+ retry: { count: number; delay: number };
66
+ staleTime: number;
67
+ refetchOnWindowFocus?: boolean;
68
+ refetchOnReconnect?: boolean;
69
+ }
70
+
71
+ // AuthProvider component props
72
+ interface AuthProviderPropsType {
73
+ children: React.ReactNode;
74
+ config?: Partial<AuthConfigType>;
75
+ onAuthChange?: (status: AuthStatusType, user: UserDetailsType | null) => void;
76
+ onError?: (error: Error) => void;
77
+ loadingComponent?: React.ReactNode;
78
+ unauthenticatedComponent?: React.ReactNode;
79
+ skipInitialCheck?: boolean;
80
+ }
81
+
82
+ // Login options
83
+ interface LoginOptionsType {
84
+ callbackUrl?: string;
85
+ params?: Record<string, string>;
86
+ }
87
+
88
+ // Logout options
89
+ interface LogoutOptionsType {
90
+ redirectUrl?: string;
91
+ callLogoutEndpoint?: boolean;
92
+ }
93
+
94
+ // Hook return type
95
+ interface UseAuthReturnType {
96
+ // User state
97
+ user: UserDetailsType | null;
98
+ staticBaseUrl: string | null;
99
+ buildId: string | null;
100
+ status: AuthStatusType;
101
+ isAuthenticated: boolean;
102
+ isLoading: boolean;
103
+
104
+ // Auth operations
105
+ login: (provider?: AuthProviderNameType, options?: LoginOptionsType) => void;
106
+ logout: (options?: LogoutOptionsType) => Promise<void>;
107
+ refreshSession: () => Promise<SessionResponseType | null>;
108
+ hasRole: (role: string) => boolean;
109
+ hasAnyRole: (roles: string[]) => boolean;
110
+
111
+ // Error state
112
+ error: Error | null;
113
+ clearError: () => void;
114
+ }
115
+ ```
116
+
117
+ ## Usage Example
118
+
119
+ ```tsx
120
+ import { useAuth, AuthProvider } from "@ram_28/kf-ai-sdk/auth";
121
+ import type {
122
+ UseAuthReturnType,
123
+ UserDetailsType,
124
+ AuthStatusType,
125
+ AuthProviderPropsType,
126
+ AuthProviderNameType,
127
+ AuthConfigType,
128
+ AuthEndpointConfigType,
129
+ LoginOptionsType,
130
+ LogoutOptionsType,
131
+ SessionResponseType,
132
+ } from "@ram_28/kf-ai-sdk/auth/types";
133
+
134
+ // Define available roles
135
+ type Role = "Admin" | "Buyer" | "Seller" | "InventoryManager";
136
+
137
+ // Auth configuration
138
+ const authConfig: Partial<AuthConfigType> = {
139
+ sessionEndpoint: "/api/id",
140
+ defaultProvider: "google",
141
+ autoRedirect: false,
142
+ sessionCheckInterval: 5 * 60 * 1000, // 5 minutes
143
+ providers: {
144
+ google: {
145
+ loginPath: "/api/auth/google/login",
146
+ logoutPath: "/api/auth/logout",
147
+ },
148
+ microsoft: {
149
+ loginPath: "/api/auth/microsoft/login",
150
+ logoutPath: "/api/auth/logout",
151
+ },
152
+ },
153
+ refetchOnWindowFocus: true,
154
+ refetchOnReconnect: true,
155
+ };
156
+
157
+ // App wrapper with AuthProvider
158
+ function App() {
159
+ // Auth status change handler
160
+ const handleAuthChange = (status: AuthStatusType, user: UserDetailsType | null) => {
161
+ console.log("Auth status:", status, "User:", user?._name);
162
+ };
163
+
164
+ // Auth error handler
165
+ const handleAuthError = (error: Error) => {
166
+ console.error("Auth error:", error.message);
167
+ };
168
+
169
+ // AuthProviderPropsType configuration
170
+ const providerProps: AuthProviderPropsType = {
171
+ children: <AppRoutes />,
172
+ config: authConfig,
173
+ onAuthChange: handleAuthChange,
174
+ onError: handleAuthError,
175
+ loadingComponent: <div>Checking authentication...</div>,
176
+ unauthenticatedComponent: <LoginPage />,
177
+ skipInitialCheck: false,
178
+ };
179
+
180
+ return <AuthProvider {...providerProps} />;
181
+ }
182
+
183
+ // Login page component
184
+ function LoginPage() {
185
+ const auth: UseAuthReturnType = useAuth();
186
+
187
+ // Login with Google
188
+ const handleGoogleLogin = () => {
189
+ const options: LoginOptionsType = {
190
+ callbackUrl: "/dashboard",
191
+ };
192
+ auth.login("google", options);
193
+ };
194
+
195
+ // Login with Microsoft
196
+ const handleMicrosoftLogin = () => {
197
+ const options: LoginOptionsType = {
198
+ callbackUrl: "/dashboard",
199
+ params: { prompt: "select_account" },
200
+ };
201
+ auth.login("microsoft", options);
202
+ };
203
+
204
+ // Login with default provider
205
+ const handleDefaultLogin = () => {
206
+ auth.login(); // Uses defaultProvider from config
207
+ };
208
+
209
+ // Access auth status
210
+ const status: AuthStatusType = auth.status;
211
+
212
+ return (
213
+ <div className="login-page">
214
+ <h1>Welcome</h1>
215
+ <p>Please sign in to continue</p>
216
+
217
+ {/* Error display */}
218
+ {auth.error && (
219
+ <div className="error">
220
+ {auth.error.message}
221
+ <button onClick={auth.clearError}>Dismiss</button>
222
+ </div>
223
+ )}
224
+
225
+ {/* Login buttons */}
226
+ <div className="login-buttons">
227
+ <button onClick={handleGoogleLogin} disabled={auth.isLoading}>
228
+ Sign in with Google
229
+ </button>
230
+ <button onClick={handleMicrosoftLogin} disabled={auth.isLoading}>
231
+ Sign in with Microsoft
232
+ </button>
233
+ <button onClick={handleDefaultLogin} disabled={auth.isLoading}>
234
+ Sign in (Default)
235
+ </button>
236
+ </div>
237
+
238
+ {/* Loading state */}
239
+ {auth.isLoading && <span>Loading...</span>}
240
+
241
+ {/* Status display */}
242
+ <p>Current status: {status}</p>
243
+ </div>
244
+ );
245
+ }
246
+
247
+ // Dashboard component (authenticated users)
248
+ function Dashboard() {
249
+ const auth: UseAuthReturnType = useAuth();
250
+
251
+ // Logout handler
252
+ const handleLogout = async () => {
253
+ const options: LogoutOptionsType = {
254
+ redirectUrl: "/login",
255
+ callLogoutEndpoint: true,
256
+ };
257
+ await auth.logout(options);
258
+ };
259
+
260
+ // Refresh session handler
261
+ const handleRefreshSession = async () => {
262
+ const session: SessionResponseType | null = await auth.refreshSession();
263
+ if (session) {
264
+ console.log("Session refreshed:", session.userDetails._name);
265
+ console.log("Static URL:", session.staticBaseUrl);
266
+ console.log("Build ID:", session.buildId);
267
+ }
268
+ };
269
+
270
+ // Access user details
271
+ const user: UserDetailsType | null = auth.user;
272
+
273
+ // Role-based access control
274
+ const isAdmin: boolean = auth.hasRole("Admin");
275
+ const canManageProducts: boolean = auth.hasAnyRole(["Admin", "Seller"]);
276
+ const canViewReports: boolean = auth.hasAnyRole(["Admin", "InventoryManager"]);
277
+
278
+ // Guard against unauthenticated access
279
+ if (!auth.isAuthenticated) {
280
+ return <div>Please log in to access the dashboard</div>;
281
+ }
282
+
283
+ return (
284
+ <div className="dashboard">
285
+ {/* Header */}
286
+ <header>
287
+ <h1>Dashboard</h1>
288
+ <div className="user-info">
289
+ <span>Welcome, {user?._name}</span>
290
+ <span>Role: {user?.Role}</span>
291
+ <button onClick={handleRefreshSession}>Refresh Session</button>
292
+ <button onClick={handleLogout}>Logout</button>
293
+ </div>
294
+ </header>
295
+
296
+ {/* Main content */}
297
+ <main>
298
+ {/* User info section */}
299
+ <section className="user-section">
300
+ <h2>User Information</h2>
301
+ <p>User ID: {user?._id}</p>
302
+ <p>Name: {user?._name}</p>
303
+ <p>Role: {user?.Role}</p>
304
+ <p>Static Base URL: {auth.staticBaseUrl}</p>
305
+ <p>Build ID: {auth.buildId}</p>
306
+ <p>Status: {auth.status}</p>
307
+ <p>Authenticated: {auth.isAuthenticated ? "Yes" : "No"}</p>
308
+ </section>
309
+
310
+ {/* Role-based sections */}
311
+ {canManageProducts && (
312
+ <section className="products-section">
313
+ <h2>Product Management</h2>
314
+ <p>You have access to manage products.</p>
315
+ </section>
316
+ )}
317
+
318
+ {canViewReports && (
319
+ <section className="reports-section">
320
+ <h2>Reports</h2>
321
+ <p>You have access to view reports.</p>
322
+ </section>
323
+ )}
324
+
325
+ {isAdmin && (
326
+ <section className="admin-section">
327
+ <h2>Admin Panel</h2>
328
+ <p>Full administrative access.</p>
329
+ </section>
330
+ )}
331
+
332
+ {!isAdmin && (
333
+ <section className="restricted-section">
334
+ <p>Admin panel is restricted to administrators.</p>
335
+ </section>
336
+ )}
337
+ </main>
338
+
339
+ {/* Error banner */}
340
+ {auth.error && (
341
+ <div className="error-banner">
342
+ Error: {auth.error.message}
343
+ <button onClick={auth.clearError}>Dismiss</button>
344
+ </div>
345
+ )}
346
+ </div>
347
+ );
348
+ }
349
+
350
+ // Protected route component
351
+ function ProtectedRoute({
352
+ children,
353
+ requiredRoles,
354
+ }: {
355
+ children: React.ReactNode;
356
+ requiredRoles?: string[];
357
+ }) {
358
+ const auth: UseAuthReturnType = useAuth();
359
+
360
+ // Show loading while checking auth
361
+ if (auth.isLoading) {
362
+ return <div>Loading...</div>;
363
+ }
364
+
365
+ // Redirect if not authenticated
366
+ if (!auth.isAuthenticated) {
367
+ return <div>Access denied. Please log in.</div>;
368
+ }
369
+
370
+ // Check role requirements
371
+ if (requiredRoles && !auth.hasAnyRole(requiredRoles)) {
372
+ return <div>Access denied. Insufficient permissions.</div>;
373
+ }
374
+
375
+ return <>{children}</>;
376
+ }
377
+
378
+ // App routes with protected routes
379
+ function AppRoutes() {
380
+ return (
381
+ <div>
382
+ {/* Public route */}
383
+ <LoginPage />
384
+
385
+ {/* Protected route - any authenticated user */}
386
+ <ProtectedRoute>
387
+ <Dashboard />
388
+ </ProtectedRoute>
389
+
390
+ {/* Protected route - admin only */}
391
+ <ProtectedRoute requiredRoles={["Admin"]}>
392
+ <div>Admin Only Content</div>
393
+ </ProtectedRoute>
394
+
395
+ {/* Protected route - multiple roles */}
396
+ <ProtectedRoute requiredRoles={["Admin", "Seller", "InventoryManager"]}>
397
+ <div>Staff Only Content</div>
398
+ </ProtectedRoute>
399
+ </div>
400
+ );
401
+ }
402
+ ```
@@ -0,0 +1,273 @@
1
+ # useFilter
2
+
3
+ ## Brief Description
4
+
5
+ - Manages filter conditions with support for nested filter groups (AND/OR/NOT operators)
6
+ - Provides a clean API for building complex filter payloads that match the backend API format
7
+ - Supports both flat conditions and nested condition groups for advanced filtering scenarios
8
+ - Includes type guards (`isCondition`, `isConditionGroup`) for safely working with the filter tree structure
9
+
10
+ ## Type Reference
11
+
12
+ ```typescript
13
+ import { useFilter, isCondition, isConditionGroup } from "@ram_28/kf-ai-sdk/filter";
14
+ import type {
15
+ UseFilterOptionsType,
16
+ UseFilterReturnType,
17
+ ConditionType,
18
+ ConditionGroupType,
19
+ ConditionOperatorType,
20
+ ConditionGroupOperatorType,
21
+ FilterType,
22
+ FilterRHSTypeType,
23
+ } from "@ram_28/kf-ai-sdk/filter/types";
24
+
25
+ // Condition operators for comparing field values
26
+ type ConditionOperatorType =
27
+ | "EQ" | "NE" | "GT" | "GTE" | "LT" | "LTE"
28
+ | "Between" | "NotBetween"
29
+ | "IN" | "NIN"
30
+ | "Empty" | "NotEmpty"
31
+ | "Contains" | "NotContains"
32
+ | "MinLength" | "MaxLength";
33
+
34
+ // Group operators for combining conditions
35
+ type ConditionGroupOperatorType = "And" | "Or" | "Not";
36
+
37
+ // RHS type for condition values
38
+ type FilterRHSTypeType = "Constant" | "BOField" | "AppVariable";
39
+
40
+ // Leaf condition (matches API format)
41
+ interface ConditionType {
42
+ id?: string;
43
+ Operator: ConditionOperatorType;
44
+ LHSField: string;
45
+ RHSValue: any;
46
+ RHSType?: FilterRHSTypeType;
47
+ }
48
+
49
+ // Condition group (recursive structure)
50
+ interface ConditionGroupType {
51
+ id?: string;
52
+ Operator: ConditionGroupOperatorType;
53
+ Condition: Array<ConditionType | ConditionGroupType>;
54
+ }
55
+
56
+ // Filter payload (alias for ConditionGroupType)
57
+ type FilterType = ConditionGroupType;
58
+
59
+ // Hook options
60
+ interface UseFilterOptionsType {
61
+ initialConditions?: Array<ConditionType | ConditionGroupType>;
62
+ initialOperator?: ConditionGroupOperatorType;
63
+ }
64
+
65
+ // Hook return type
66
+ interface UseFilterReturnType {
67
+ // State (read-only)
68
+ operator: ConditionGroupOperatorType;
69
+ items: Array<ConditionType | ConditionGroupType>;
70
+ payload: FilterType | undefined;
71
+ hasConditions: boolean;
72
+
73
+ // Add operations (return id of created item)
74
+ addCondition: (condition: Omit<ConditionType, "id">, parentId?: string) => string;
75
+ addConditionGroup: (operator: ConditionGroupOperatorType, parentId?: string) => string;
76
+
77
+ // Update operations
78
+ updateCondition: (id: string, updates: Partial<Omit<ConditionType, "id">>) => void;
79
+ updateGroupOperator: (id: string, operator: ConditionGroupOperatorType) => void;
80
+
81
+ // Remove & access
82
+ removeCondition: (id: string) => void;
83
+ getCondition: (id: string) => ConditionType | ConditionGroupType | undefined;
84
+
85
+ // Utility
86
+ clearAllConditions: () => void;
87
+ setRootOperator: (op: ConditionGroupOperatorType) => void;
88
+ }
89
+
90
+ // Type guards
91
+ const isCondition: (item: ConditionType | ConditionGroupType) => item is ConditionType;
92
+ const isConditionGroup: (item: ConditionType | ConditionGroupType) => item is ConditionGroupType;
93
+ ```
94
+
95
+ ## Usage Example
96
+
97
+ ```tsx
98
+ import { useFilter, isCondition, isConditionGroup } from "@ram_28/kf-ai-sdk/filter";
99
+ import type {
100
+ ConditionType,
101
+ ConditionGroupType,
102
+ ConditionGroupOperatorType,
103
+ FilterType,
104
+ UseFilterOptionsType,
105
+ UseFilterReturnType,
106
+ } from "@ram_28/kf-ai-sdk/filter/types";
107
+
108
+ function ProductFilterBuilder() {
109
+ // Initialize hook with options
110
+ const options: UseFilterOptionsType = {
111
+ initialOperator: "And",
112
+ };
113
+ const filter: UseFilterReturnType = useFilter(options);
114
+
115
+ // Add a simple condition at root level
116
+ const handleAddCondition = () => {
117
+ const id = filter.addCondition({
118
+ Operator: "EQ",
119
+ LHSField: "Category",
120
+ RHSValue: "Electronics",
121
+ RHSType: "Constant",
122
+ });
123
+ console.log("Created condition with id:", id);
124
+ };
125
+
126
+ // Build nested filter: (Category = "Electronics") AND (Price > 100 OR OnSale = true)
127
+ const handleBuildComplexFilter = () => {
128
+ filter.clearAllConditions();
129
+
130
+ // Add root condition
131
+ filter.addCondition({
132
+ Operator: "EQ",
133
+ LHSField: "Category",
134
+ RHSValue: "Electronics",
135
+ });
136
+
137
+ // Create nested OR group
138
+ const groupId = filter.addConditionGroup("Or");
139
+
140
+ // Add conditions to the group
141
+ filter.addCondition({
142
+ Operator: "GT",
143
+ LHSField: "Price",
144
+ RHSValue: 100,
145
+ }, groupId);
146
+
147
+ const saleConditionId = filter.addCondition({
148
+ Operator: "EQ",
149
+ LHSField: "OnSale",
150
+ RHSValue: true,
151
+ }, groupId);
152
+
153
+ // Update a condition
154
+ filter.updateCondition(saleConditionId, { RHSValue: false });
155
+
156
+ // Toggle group operator
157
+ filter.updateGroupOperator(groupId, "And");
158
+ };
159
+
160
+ // Render filter tree recursively
161
+ const renderFilterItem = (item: ConditionType | ConditionGroupType, depth = 0): JSX.Element => {
162
+ const indent = { marginLeft: depth * 20 };
163
+
164
+ if (isCondition(item)) {
165
+ return (
166
+ <div key={item.id} style={indent} className="filter-condition">
167
+ <span>
168
+ {item.LHSField} {item.Operator} {String(item.RHSValue)}
169
+ </span>
170
+ <button onClick={() => filter.removeCondition(item.id!)}>Remove</button>
171
+ <button onClick={() => filter.updateCondition(item.id!, { RHSValue: "Updated" })}>
172
+ Update
173
+ </button>
174
+ </div>
175
+ );
176
+ }
177
+
178
+ if (isConditionGroup(item)) {
179
+ return (
180
+ <div key={item.id} style={indent} className="filter-group">
181
+ <div className="group-header">
182
+ <select
183
+ value={item.Operator}
184
+ onChange={(e) =>
185
+ filter.updateGroupOperator(item.id!, e.target.value as ConditionGroupOperatorType)
186
+ }
187
+ >
188
+ <option value="And">AND</option>
189
+ <option value="Or">OR</option>
190
+ <option value="Not">NOT</option>
191
+ </select>
192
+ <button onClick={() => filter.addCondition({ Operator: "EQ", LHSField: "Field", RHSValue: "" }, item.id!)}>
193
+ + Condition
194
+ </button>
195
+ <button onClick={() => filter.addConditionGroup("And", item.id!)}>+ Group</button>
196
+ <button onClick={() => filter.removeCondition(item.id!)}>Remove Group</button>
197
+ </div>
198
+ <div className="group-conditions">
199
+ {item.Condition.map((child) => renderFilterItem(child, depth + 1))}
200
+ </div>
201
+ </div>
202
+ );
203
+ }
204
+
205
+ return <></>;
206
+ };
207
+
208
+ // Use filter payload in API call
209
+ const handleApplyFilter = async () => {
210
+ const payload: FilterType | undefined = filter.payload;
211
+ if (payload) {
212
+ console.log("API Payload (no ids):", JSON.stringify(payload, null, 2));
213
+ const response = await fetch("/api/products", {
214
+ method: "POST",
215
+ headers: { "Content-Type": "application/json" },
216
+ body: JSON.stringify({ Filter: payload }),
217
+ });
218
+ return response.json();
219
+ }
220
+ };
221
+
222
+ // Access individual item by id
223
+ const handleGetItem = (id: string) => {
224
+ const item = filter.getCondition(id);
225
+ if (item) {
226
+ console.log("Found item:", item);
227
+ }
228
+ };
229
+
230
+ return (
231
+ <div className="filter-builder">
232
+ {/* Root operator control */}
233
+ <div className="root-controls">
234
+ <label>
235
+ Root Operator:
236
+ <select
237
+ value={filter.operator}
238
+ onChange={(e) => filter.setRootOperator(e.target.value as ConditionGroupOperator)}
239
+ >
240
+ <option value="And">AND</option>
241
+ <option value="Or">OR</option>
242
+ </select>
243
+ </label>
244
+ <button onClick={handleAddCondition}>Add Condition</button>
245
+ <button onClick={() => filter.addConditionGroup("Or")}>Add Group</button>
246
+ <button onClick={handleBuildComplexFilter}>Build Complex Filter</button>
247
+ <button onClick={filter.clearAllConditions} disabled={!filter.hasConditions}>
248
+ Clear All
249
+ </button>
250
+ </div>
251
+
252
+ {/* Filter tree display */}
253
+ <div className="filter-tree">
254
+ <h3>Filter Tree ({filter.items.length} root items)</h3>
255
+ {filter.items.map((item) => renderFilterItem(item))}
256
+ </div>
257
+
258
+ {/* Apply filter */}
259
+ <div className="filter-actions">
260
+ <button onClick={handleApplyFilter} disabled={!filter.hasConditions}>
261
+ Apply Filter
262
+ </button>
263
+ <span>Has conditions: {filter.hasConditions ? "Yes" : "No"}</span>
264
+ </div>
265
+
266
+ {/* Debug payload (id fields are stripped) */}
267
+ <pre className="filter-payload">
268
+ {JSON.stringify(filter.payload, null, 2)}
269
+ </pre>
270
+ </div>
271
+ );
272
+ }
273
+ ```