@base44/sdk 0.1.0

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,526 @@
1
+ # Base44 JavaScript SDK
2
+
3
+ A modern JavaScript SDK for interacting with the Base44 API. Designed to work with both JavaScript and TypeScript projects.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @base44/sdk
9
+ # or
10
+ yarn add @base44/sdk
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### Basic Setup
16
+
17
+ ```javascript
18
+ import { createClient } from '@base44/sdk';
19
+
20
+ // Create a client instance
21
+ const base44 = createClient({
22
+ serverUrl: 'https://app.base44.com', // Optional, defaults to 'https://app.base44.com'
23
+ appId: 'your-app-id', // Required
24
+ env: 'prod', // Optional, defaults to 'prod'
25
+ token: 'your-token', // Optional
26
+ autoInitAuth: true, // Optional, defaults to true - auto-detects tokens from URL or localStorage
27
+ });
28
+ ```
29
+
30
+ ### Working with Entities
31
+
32
+ ```javascript
33
+ // List all products
34
+ const products = await base44.entities.Product.list();
35
+
36
+ // Filter products by category
37
+ const filteredProducts = await base44.entities.Product.filter({
38
+ category: ['electronics', 'computers']
39
+ });
40
+
41
+ // Get a specific product
42
+ const product = await base44.entities.Product.get('product-id');
43
+
44
+ // Create a new product
45
+ const newProduct = await base44.entities.Product.create({
46
+ name: 'New Product',
47
+ price: 99.99,
48
+ category: 'electronics'
49
+ });
50
+
51
+ // Update a product
52
+ const updatedProduct = await base44.entities.Product.update('product-id', {
53
+ price: 89.99
54
+ });
55
+
56
+ // Delete a product
57
+ await base44.entities.Product.delete('product-id');
58
+
59
+ // Bulk create products
60
+ const newProducts = await base44.entities.Product.bulkCreate([
61
+ { name: 'Product 1', price: 19.99 },
62
+ { name: 'Product 2', price: 29.99 }
63
+ ]);
64
+ ```
65
+
66
+ ### Working with Integrations
67
+
68
+ ```javascript
69
+ // Send an email using the Core integration
70
+ const emailResult = await base44.integrations.Core.SendEmail({
71
+ to: 'user@example.com',
72
+ subject: 'Hello from Base44',
73
+ body: 'This is a test email sent via the Base44 SDK'
74
+ });
75
+
76
+ // Use a custom integration
77
+ const result = await base44.integrations.CustomPackage.CustomEndpoint({
78
+ param1: 'value1',
79
+ param2: 'value2'
80
+ });
81
+
82
+ // Upload a file
83
+ const fileInput = document.querySelector('input[type="file"]');
84
+ const file = fileInput.files[0];
85
+ const uploadResult = await base44.integrations.Core.UploadFile({
86
+ file,
87
+ metadata: { type: 'profile-picture' }
88
+ });
89
+ ```
90
+
91
+ ## Authentication
92
+
93
+ The SDK provides comprehensive authentication capabilities to help you build secure applications.
94
+
95
+ ### Creating an Authenticated Client
96
+
97
+ To create a client with authentication:
98
+
99
+ ```javascript
100
+ import { createClient } from '@base44/sdk';
101
+ import { getAccessToken } from '@base44/sdk/utils/auth-utils';
102
+
103
+ // Create a client with authentication
104
+ const base44 = createClient({
105
+ appId: 'your-app-id',
106
+ accessToken: getAccessToken() // Automatically retrieves token from localStorage or URL
107
+ });
108
+
109
+ // Check authentication status
110
+ const isAuthenticated = await base44.auth.isAuthenticated();
111
+ console.log('Authenticated:', isAuthenticated);
112
+
113
+ // Get current user information (requires authentication)
114
+ if (isAuthenticated) {
115
+ const user = await base44.auth.me();
116
+ console.log('Current user:', user);
117
+ }
118
+ ```
119
+
120
+ ### Login and Logout
121
+
122
+ ```javascript
123
+ import { createClient } from '@base44/sdk';
124
+ import { getAccessToken, saveAccessToken, removeAccessToken } from '@base44/sdk/utils/auth-utils';
125
+
126
+ const base44 = createClient({ appId: 'your-app-id' });
127
+
128
+ // Redirect to the login page
129
+ // This will redirect to: app.base44.com/login?from_url=http://your-app.com/dashboard&app_id=your-app-id
130
+ function handleLogin() {
131
+ base44.auth.login('/dashboard');
132
+ }
133
+
134
+ // Handle successful login (on return from Base44 login)
135
+ function handleLoginReturn() {
136
+ const token = getAccessToken();
137
+ if (token) {
138
+ console.log('Successfully logged in with token:', token);
139
+ // The token is automatically saved to localStorage and removed from URL
140
+ }
141
+ }
142
+
143
+ // Logout
144
+ function handleLogout() {
145
+ removeAccessToken();
146
+ window.location.href = '/login';
147
+ }
148
+ ```
149
+
150
+ ### Real-World Authentication Example (React)
151
+
152
+ Here's a complete example of implementing Base44 authentication in a React application:
153
+
154
+ ```jsx
155
+ import React, { createContext, useContext, useEffect, useState } from 'react';
156
+ import { Navigate, Outlet, Route, Routes, useLocation } from 'react-router-dom';
157
+ import { createClient } from '@base44/sdk';
158
+ import { getAccessToken, removeAccessToken } from '@base44/sdk/utils/auth-utils';
159
+
160
+ // Create AuthContext
161
+ const AuthContext = createContext(null);
162
+
163
+ // Auth Provider Component
164
+ function AuthProvider({ children }) {
165
+ const [user, setUser] = useState(null);
166
+ const [loading, setLoading] = useState(true);
167
+ const [client] = useState(() =>
168
+ createClient({
169
+ appId: 'your-app-id',
170
+ accessToken: getAccessToken()
171
+ })
172
+ );
173
+
174
+ useEffect(() => {
175
+ async function loadUser() {
176
+ try {
177
+ const isAuth = await client.auth.isAuthenticated();
178
+ if (isAuth) {
179
+ const userData = await client.auth.me();
180
+ setUser(userData);
181
+ }
182
+ } catch (error) {
183
+ console.error('Authentication error:', error);
184
+ } finally {
185
+ setLoading(false);
186
+ }
187
+ }
188
+
189
+ loadUser();
190
+ }, [client]);
191
+
192
+ const login = () => {
193
+ client.auth.login(window.location.pathname);
194
+ };
195
+
196
+ const logout = () => {
197
+ removeAccessToken();
198
+ setUser(null);
199
+ window.location.href = '/login';
200
+ };
201
+
202
+ return (
203
+ <AuthContext.Provider value={{ user, loading, client, login, logout }}>
204
+ {children}
205
+ </AuthContext.Provider>
206
+ );
207
+ }
208
+
209
+ // Custom hook to use auth context
210
+ function useAuth() {
211
+ const context = useContext(AuthContext);
212
+ if (!context) {
213
+ throw new Error('useAuth must be used within an AuthProvider');
214
+ }
215
+ return context;
216
+ }
217
+
218
+ // Protected Route Component
219
+ function ProtectedRoute() {
220
+ const { user, loading, login } = useAuth();
221
+ const location = useLocation();
222
+
223
+ // Check if we're returning from login with a token in URL
224
+ useEffect(() => {
225
+ const token = getAccessToken(); // This will save token from URL if present
226
+ if (token && !user && !loading) {
227
+ window.location.reload(); // Reload to apply the new token
228
+ }
229
+ }, [location, user, loading]);
230
+
231
+ if (loading) {
232
+ return <div>Loading...</div>;
233
+ }
234
+
235
+ if (!user) {
236
+ // If not authenticated, redirect to login
237
+ login();
238
+ return <div>Redirecting to login...</div>;
239
+ }
240
+
241
+ // If authenticated, render the child routes
242
+ return <Outlet />;
243
+ }
244
+
245
+ // Dashboard Component (protected)
246
+ function Dashboard() {
247
+ const { user, client, logout } = useAuth();
248
+ const [todos, setTodos] = useState([]);
249
+ const [loading, setLoading] = useState(true);
250
+
251
+ useEffect(() => {
252
+ async function loadTodos() {
253
+ try {
254
+ // Load user-specific data using the SDK
255
+ const items = await client.entities.Todo.filter({
256
+ assignee: user.id
257
+ });
258
+ setTodos(items);
259
+ } catch (error) {
260
+ console.error('Failed to load todos:', error);
261
+ } finally {
262
+ setLoading(false);
263
+ }
264
+ }
265
+
266
+ loadTodos();
267
+ }, [client, user]);
268
+
269
+ return (
270
+ <div>
271
+ <h1>Welcome, {user.name}!</h1>
272
+ <button onClick={logout}>Logout</button>
273
+
274
+ <h2>Your Todos</h2>
275
+ {loading ? (
276
+ <div>Loading todos...</div>
277
+ ) : (
278
+ <ul>
279
+ {todos.map(todo => (
280
+ <li key={todo.id}>{todo.title}</li>
281
+ ))}
282
+ </ul>
283
+ )}
284
+ </div>
285
+ );
286
+ }
287
+
288
+ // Login Page
289
+ function LoginPage() {
290
+ const { login, user } = useAuth();
291
+
292
+ if (user) {
293
+ return <Navigate to="/dashboard" />;
294
+ }
295
+
296
+ return (
297
+ <div>
298
+ <h1>Login Required</h1>
299
+ <button onClick={login}>Login with Base44</button>
300
+ </div>
301
+ );
302
+ }
303
+
304
+ // App Component
305
+ function App() {
306
+ return (
307
+ <AuthProvider>
308
+ <Routes>
309
+ <Route path="/login" element={<LoginPage />} />
310
+ <Route element={<ProtectedRoute />}>
311
+ <Route path="/dashboard" element={<Dashboard />} />
312
+ <Route path="/profile" element={<ProfilePage />} />
313
+ {/* Add more protected routes here */}
314
+ </Route>
315
+ <Route path="/" element={<Navigate to="/dashboard" />} />
316
+ </Routes>
317
+ </AuthProvider>
318
+ );
319
+ }
320
+ ```
321
+
322
+ ## TypeScript Support
323
+
324
+ This SDK includes TypeScript definitions out of the box:
325
+
326
+ ```typescript
327
+ import { createClient, Base44Error } from '@base44/sdk';
328
+ import type { Entity, Base44Client, AuthModule } from '@base44/sdk';
329
+
330
+ // Create a typed client
331
+ const base44: Base44Client = createClient({
332
+ appId: 'your-app-id'
333
+ });
334
+
335
+ // Using the entities module with type safety
336
+ async function fetchProducts() {
337
+ try {
338
+ const products: Entity[] = await base44.entities.Product.list();
339
+ console.log(products.map(p => p.name));
340
+
341
+ const product: Entity = await base44.entities.Product.get('product-id');
342
+ console.log(product.name);
343
+ } catch (error) {
344
+ if (error instanceof Base44Error) {
345
+ console.error(`Error ${error.status}: ${error.message}`);
346
+ }
347
+ }
348
+ }
349
+
350
+ // Authentication with TypeScript
351
+ async function handleAuth(auth: AuthModule) {
352
+ // Check authentication
353
+ const isAuthenticated: boolean = await auth.isAuthenticated();
354
+
355
+ if (isAuthenticated) {
356
+ // Get user info
357
+ const user: Entity = await auth.me();
358
+ console.log(`Logged in as: ${user.name}, Role: ${user.role}`);
359
+
360
+ // Update user
361
+ const updatedUser: Entity = await auth.updateMe({
362
+ preferences: { theme: 'dark' }
363
+ });
364
+ } else {
365
+ // Redirect to login
366
+ auth.login('/dashboard');
367
+ }
368
+ }
369
+
370
+ // Execute with proper typing
371
+ handleAuth(base44.auth);
372
+ ```
373
+
374
+ ### Advanced TypeScript Usage
375
+
376
+ You can define your own entity interfaces for better type safety:
377
+
378
+ ```typescript
379
+ // Define custom entity interfaces
380
+ interface User extends Entity {
381
+ name: string;
382
+ email: string;
383
+ role: 'admin' | 'editor' | 'viewer';
384
+ preferences?: {
385
+ theme: 'light' | 'dark';
386
+ notifications: boolean;
387
+ };
388
+ }
389
+
390
+ interface Product extends Entity {
391
+ name: string;
392
+ price: number;
393
+ category: string;
394
+ inStock: boolean;
395
+ }
396
+
397
+ // Use your custom interfaces with the SDK
398
+ async function getLoggedInUser(): Promise<User | null> {
399
+ const base44 = createClient({ appId: 'your-app-id' });
400
+
401
+ try {
402
+ const user = await base44.auth.me() as User;
403
+ return user;
404
+ } catch (error) {
405
+ console.error('Failed to get user:', error);
406
+ return null;
407
+ }
408
+ }
409
+
410
+ // Use with React hooks
411
+ function useBase44User() {
412
+ const [user, setUser] = useState<User | null>(null);
413
+ const [loading, setLoading] = useState<boolean>(true);
414
+
415
+ useEffect(() => {
416
+ const base44 = createClient({ appId: 'your-app-id' });
417
+
418
+ async function fetchUser() {
419
+ try {
420
+ const userData = await base44.auth.me() as User;
421
+ setUser(userData);
422
+ } catch (error) {
423
+ console.error('Auth error:', error);
424
+ } finally {
425
+ setLoading(false);
426
+ }
427
+ }
428
+
429
+ fetchUser();
430
+ }, []);
431
+
432
+ return { user, loading };
433
+ }
434
+ ```
435
+
436
+ ## Error Handling
437
+
438
+ The SDK provides a custom `Base44Error` class for error handling:
439
+
440
+ ```javascript
441
+ import { createClient, Base44Error } from '@base44/sdk';
442
+
443
+ const base44 = createClient({ appId: 'your-app-id' });
444
+
445
+ try {
446
+ const result = await base44.entities.NonExistentEntity.list();
447
+ } catch (error) {
448
+ if (error instanceof Base44Error) {
449
+ console.error(`Status: ${error.status}`);
450
+ console.error(`Message: ${error.message}`);
451
+ console.error(`Code: ${error.code}`);
452
+ console.error(`Data: ${JSON.stringify(error.data)}`);
453
+ } else {
454
+ console.error('Unexpected error:', error);
455
+ }
456
+ }
457
+ ```
458
+
459
+ ## Testing
460
+
461
+ The SDK includes comprehensive tests to ensure reliability.
462
+
463
+ ### Running Tests
464
+
465
+ ```bash
466
+ # Run all tests
467
+ npm test
468
+
469
+ # Run unit tests only (no API calls)
470
+ npm run test:unit
471
+
472
+ # Run end-to-end tests (requires API access)
473
+ npm run test:e2e
474
+
475
+ # Run tests with coverage report
476
+ npm run test:coverage
477
+ ```
478
+
479
+ ### Setting Up E2E Tests
480
+
481
+ E2E tests require access to a Base44 API. To run these tests:
482
+
483
+ 1. Copy `tests/.env.example` to `tests/.env`
484
+ 2. Fill in your Base44 API credentials in the `.env` file:
485
+ ```
486
+ BASE44_SERVER_URL=https://app.base44.com
487
+ BASE44_APP_ID=your_app_id_here
488
+ BASE44_AUTH_TOKEN=your_auth_token_here
489
+ ```
490
+
491
+ 3. Optionally, set `SKIP_E2E_TESTS=true` to skip E2E tests.
492
+
493
+ ### Writing Your Own Tests
494
+
495
+ You can use the provided test utilities for writing your own tests:
496
+
497
+ ```javascript
498
+ const { createClient } = require('@base44/sdk');
499
+ const { getTestConfig } = require('@base44/sdk/tests/utils/test-config');
500
+
501
+ describe('My Tests', () => {
502
+ let base44;
503
+
504
+ beforeAll(() => {
505
+ const config = getTestConfig();
506
+ base44 = createClient({
507
+ serverUrl: config.serverUrl,
508
+ appId: config.appId,
509
+ });
510
+
511
+ if (config.token) {
512
+ base44.setToken(config.token);
513
+ }
514
+ });
515
+
516
+ test('My test', async () => {
517
+ const todos = await base44.entities.Todo.filter({}, 10);
518
+ expect(Array.isArray(todos)).toBe(true);
519
+ expect(todos.length).toBeGreaterThan(0);
520
+ });
521
+ });
522
+ ```
523
+
524
+ ## License
525
+
526
+ MIT
@@ -0,0 +1,86 @@
1
+ export interface ClientConfig {
2
+ serverUrl?: string;
3
+ appId: string | number;
4
+ env?: 'prod' | 'dev';
5
+ token?: string;
6
+ requiresAuth?: boolean;
7
+ }
8
+
9
+ export interface Entity {
10
+ id: string;
11
+ [key: string]: any;
12
+ }
13
+
14
+ export interface FilterOptions {
15
+ sort?: string;
16
+ limit?: number;
17
+ skip?: number;
18
+ fields?: string[] | string;
19
+ }
20
+
21
+ export interface EntityMethods {
22
+ list(sort?: string, limit?: number, skip?: number, fields?: string[] | string): Promise<Entity[]>;
23
+ filter(query: any, sort?: string, limit?: number, skip?: number, fields?: string[] | string): Promise<Entity[]>;
24
+ get(id: string): Promise<Entity>;
25
+ create(data: Record<string, any>): Promise<Entity>;
26
+ update(id: string, data: Record<string, any>): Promise<Entity>;
27
+ delete(id: string): Promise<void>;
28
+ deleteMany(query: Record<string, any>): Promise<void>;
29
+ bulkCreate(data: Record<string, any>[]): Promise<Entity[]>;
30
+ importEntities(file: File): Promise<any>;
31
+ }
32
+
33
+ export interface EntitiesModule {
34
+ [entityName: string]: EntityMethods;
35
+ }
36
+
37
+ export interface IntegrationEndpoint {
38
+ (data: Record<string, any>): Promise<any>;
39
+ }
40
+
41
+ export interface IntegrationsPackage {
42
+ [endpointName: string]: IntegrationEndpoint;
43
+ }
44
+
45
+ export interface IntegrationsModule {
46
+ [packageName: string]: IntegrationsPackage;
47
+ }
48
+
49
+ export interface AuthModule {
50
+ me(): Promise<Entity>;
51
+ updateMe(data: Record<string, any>): Promise<Entity>;
52
+ login(nextUrl?: string): void;
53
+ logout(redirectUrl?: string): Promise<void>;
54
+ setToken(token: string, saveToStorage?: boolean): void;
55
+ isAuthenticated(): Promise<boolean>;
56
+ }
57
+
58
+ export interface Base44Client {
59
+ entities: EntitiesModule;
60
+ integrations: IntegrationsModule;
61
+ auth: AuthModule;
62
+ setToken(token: string): void;
63
+ getConfig(): {
64
+ serverUrl: string;
65
+ appId: string | number;
66
+ env: string;
67
+ requiresAuth: boolean;
68
+ };
69
+ }
70
+
71
+ export class Base44Error extends Error {
72
+ status?: number;
73
+ code?: string;
74
+ data?: any;
75
+ originalError?: Error;
76
+
77
+ constructor(
78
+ message: string,
79
+ status?: number,
80
+ code?: string,
81
+ data?: any,
82
+ originalError?: Error
83
+ );
84
+ }
85
+
86
+ export function createClient(config: ClientConfig): Base44Client;