@basictech/react 0.7.0-beta.5 → 0.7.0-beta.7

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @basictech/react
2
2
 
3
- A React package for integrating Basic authentication and database functionality into your React applications.
3
+ React SDK for [Basic](https://basic.tech) - add authentication and real-time database to your React app in minutes.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,298 +8,292 @@ A React package for integrating Basic authentication and database functionality
8
8
  npm install @basictech/react
9
9
  ```
10
10
 
11
- ## Usage
11
+ ## Quick Start
12
12
 
13
- ### 1. Wrap your application with BasicProvider
13
+ ### 1. Create a Schema
14
14
 
15
- In your root component or App.tsx, wrap your application with the `BasicProvider`:
15
+ Create a `basic.config.ts` file with your project configuration:
16
16
 
17
17
  ```typescript
18
- import { BasicProvider } from '@basictech/react';
19
-
20
- const schema = {
21
- tables: {
22
- todos: {
23
- fields: {
24
- id: {
25
- type: "string",
26
- primary: true
27
- },
28
- title: {
29
- type: "string",
30
- indexed: true
31
- },
32
- completed: {
33
- type: "boolean",
34
- indexed: true
35
- }
18
+ export const schema = {
19
+ project_id: "YOUR_PROJECT_ID",
20
+ version: 1,
21
+ tables: {
22
+ todos: {
23
+ type: "collection",
24
+ fields: {
25
+ title: { type: "string", indexed: true },
26
+ completed: { type: "boolean", indexed: true }
36
27
  }
37
28
  }
38
- }
29
+ }
39
30
  }
31
+ ```
32
+
33
+ ### 2. Add the Provider
40
34
 
35
+ Wrap your app with `BasicProvider`:
36
+
37
+ ```tsx
38
+ import { BasicProvider } from '@basictech/react'
39
+ import { schema } from './basic.config'
41
40
 
42
41
  function App() {
43
42
  return (
44
- <BasicProvider project_id="YOUR_PROJECT_ID" schema={schema} debug>
45
- {/* Your app components */}
43
+ <BasicProvider schema={schema}>
44
+ <YourApp />
46
45
  </BasicProvider>
47
- );
46
+ )
48
47
  }
49
-
50
- export default App;
51
48
  ```
52
49
 
53
- Replace `YOUR_PROJECT_ID` with your actual Basic project ID.
50
+ ### 3. Use the Hook
54
51
 
55
- ### 2. Use the useBasic hook
52
+ Access auth and database in any component:
56
53
 
57
- In your components, you can use the `useBasic` hook to access authentication and database functionality:
54
+ ```tsx
55
+ import { useBasic, useQuery } from '@basictech/react'
58
56
 
59
- ```typescript
60
- import { useBasic } from '@basictech/react';
57
+ function TodoList() {
58
+ const { db, isSignedIn, signIn, signOut, user } = useBasic()
59
+
60
+ // Live query - automatically updates when data changes
61
+ const todos = useQuery(() => db.collection('todos').getAll())
61
62
 
62
- function MyComponent() {
63
- const { user, isSignedIn, signin, signout, signinWithCode, db } = useBasic();
63
+ const addTodo = async () => {
64
+ await db.collection('todos').add({
65
+ title: 'New todo',
66
+ completed: false
67
+ })
68
+ }
64
69
 
65
70
  if (!isSignedIn) {
66
- return <button onClick={signin}>Sign In</button>;
71
+ return <button onClick={signIn}>Sign In</button>
67
72
  }
68
73
 
69
74
  return (
70
75
  <div>
71
- <h1>Welcome, {user.name}!</h1>
72
- <button onClick={signout}>Sign Out</button>
76
+ <p>Welcome, {user?.email}</p>
77
+ <button onClick={addTodo}>Add Todo</button>
78
+ <ul>
79
+ {todos?.map(todo => (
80
+ <li key={todo.id}>{todo.title}</li>
81
+ ))}
82
+ </ul>
83
+ <button onClick={signOut}>Sign Out</button>
73
84
  </div>
74
- );
85
+ )
75
86
  }
76
87
  ```
77
88
 
78
- ### 3. Manual OAuth Code Handling
79
-
80
- For custom OAuth flows (mobile apps, server-side redirects, etc.), you can manually handle authorization codes:
89
+ ---
81
90
 
82
- ```typescript
83
- import { useBasic } from '@basictech/react';
84
-
85
- function CustomAuthComponent() {
86
- const { signinWithCode } = useBasic();
87
-
88
- const handleOAuthCode = async (code: string, state?: string) => {
89
- const result = await signinWithCode(code, state);
90
-
91
- if (result.success) {
92
- console.log('Successfully authenticated!');
93
- // Redirect to authenticated area
94
- } else {
95
- console.error('Authentication failed:', result.error);
96
- // Handle error
97
- }
98
- };
99
-
100
- // Example: Handle OAuth code from URL parameters
101
- useEffect(() => {
102
- const urlParams = new URLSearchParams(window.location.search);
103
- const code = urlParams.get('code');
104
- const state = urlParams.get('state');
105
-
106
- if (code) {
107
- handleOAuthCode(code, state || undefined);
108
- }
109
- }, []);
91
+ ## API Reference
110
92
 
111
- return <div>Processing authentication...</div>;
112
- }
113
- ```
93
+ ### `<BasicProvider>`
114
94
 
115
- #### Mobile App Integration Example:
95
+ Root provider component. Must wrap your entire app.
116
96
 
117
- ```typescript
118
- // React Native or mobile app deep link handling
119
- const handleDeepLink = (url: string) => {
120
- const urlParams = new URLSearchParams(url.split('?')[1]);
121
- const code = urlParams.get('code');
122
- const state = urlParams.get('state');
123
-
124
- if (code) {
125
- signinWithCode(code, state || undefined);
126
- }
127
- };
97
+ ```tsx
98
+ <BasicProvider
99
+ schema={schema} // Required: Your Basic schema
100
+ debug={false} // Optional: Enable console logging
101
+ dbMode="sync" // Optional: "sync" (default) or "remote"
102
+ />
128
103
  ```
129
104
 
130
- #### Server-Side OAuth Example:
105
+ #### Props
131
106
 
132
- ```typescript
133
- // When OAuth happens server-side and you receive the code
134
- const handleServerOAuth = async (serverCode: string) => {
135
- const result = await signinWithCode(serverCode);
136
-
137
- if (result.success) {
138
- // Redirect to authenticated area
139
- window.location.href = '/dashboard';
140
- } else {
141
- // Show error message
142
- alert(`Authentication failed: ${result.error}`);
143
- }
144
- };
145
- ```
107
+ | Prop | Type | Default | Description |
108
+ |------|------|---------|-------------|
109
+ | `schema` | `object` | required | Schema with `project_id` and `tables` |
110
+ | `debug` | `boolean` | `false` | Enable debug logging |
111
+ | `dbMode` | `"sync" \| "remote"` | `"sync"` | Database mode |
146
112
 
147
- ### 4. Custom OAuth Redirect URIs
113
+ #### Database Modes
148
114
 
149
- You can specify custom redirect URIs for OAuth flows:
115
+ - **`sync`** - Local-first with IndexedDB + real-time sync via WebSocket
116
+ - **`remote`** - Direct REST API calls (no local storage)
150
117
 
151
- ```typescript
152
- import { useBasic } from '@basictech/react';
153
-
154
- function CustomAuthComponent() {
155
- const { getSignInLink } = useBasic();
118
+ ---
156
119
 
157
- const handleCustomRedirect = () => {
158
- // Use a custom redirect URI
159
- const signInUrl = getSignInLink('https://yourapp.com/auth/callback');
160
- window.location.href = signInUrl;
161
- };
120
+ ### `useBasic()`
162
121
 
163
- const handleDefaultRedirect = () => {
164
- // Use default redirect (current page URL)
165
- const signInUrl = getSignInLink();
166
- window.location.href = signInUrl;
167
- };
122
+ Main hook for accessing auth and database.
168
123
 
169
- return (
170
- <div>
171
- <button onClick={handleCustomRedirect}>
172
- Sign In (Custom Redirect)
173
- </button>
174
- <button onClick={handleDefaultRedirect}>
175
- Sign In (Default Redirect)
176
- </button>
177
- </div>
178
- );
179
- }
124
+ ```tsx
125
+ const {
126
+ // Auth state
127
+ isReady, // boolean - SDK initialized
128
+ isSignedIn, // boolean - User authenticated
129
+ user, // { id, email, ... } | null
130
+
131
+ // Auth methods
132
+ signIn, // () => void - Redirect to login
133
+ signOut, // () => void - Clear session
134
+ signInWithCode, // (code, state?) => Promise - Manual OAuth
135
+ getSignInUrl, // (redirectUri?) => string - Get OAuth URL
136
+ getToken, // () => Promise<string> - Get access token
137
+
138
+ // Database
139
+ db, // Database instance
140
+ dbStatus, // "OFFLINE" | "CONNECTING" | "ONLINE" | "SYNCING"
141
+ dbMode, // "sync" | "remote"
142
+ } = useBasic()
180
143
  ```
181
144
 
182
- #### Use Cases for Custom Redirect URIs:
145
+ ---
183
146
 
184
- - **Mobile Apps**: Redirect to app-specific URLs
185
- - **Multi-Domain**: Redirect to different domains based on context
186
- - **Testing**: Use test-specific callback URLs
187
- - **Subdomains**: Redirect to specific subdomains
147
+ ### `useQuery()`
188
148
 
189
- ## API Reference
149
+ Live query hook - automatically re-renders when data changes.
190
150
 
151
+ ```tsx
152
+ import { useQuery } from '@basictech/react'
191
153
 
192
- ### <BasicProvider>
154
+ // Get all items
155
+ const todos = useQuery(() => db.collection('todos').getAll())
193
156
 
194
- The `BasicProvider` component accepts the following props:
157
+ // With type safety
158
+ interface Todo {
159
+ id: string
160
+ title: string
161
+ completed: boolean
162
+ }
163
+ const todos = useQuery(() => db.collection<Todo>('todos').getAll())
164
+ ```
195
165
 
196
- - `project_id` (required): String - Your Basic project ID.
197
- - `schema` (required): Object - The schema definition for your database.
198
- - `debug` (optional): Boolean - Enable debug mode for additional logging. Default is `false`.
199
- - `children` (required): React.ReactNode - The child components to be wrapped by the provider.
166
+ > **Note:** Only works in `sync` mode. In `remote` mode, use manual fetching.
200
167
 
168
+ ---
201
169
 
170
+ ### Database Methods
202
171
 
172
+ #### `db.collection(name)`
203
173
 
204
- ### useQuery
174
+ Access a collection by name.
205
175
 
206
- returns a react hook that will automatically update data based on your query
176
+ ```tsx
177
+ const { db } = useBasic()
178
+ const todos = db.collection('todos')
179
+ ```
207
180
 
208
- usage:
181
+ #### Collection Methods
209
182
 
210
- ```typescript
211
- import { useQuery } from '@basictech/react'
183
+ | Method | Returns | Description |
184
+ |--------|---------|-------------|
185
+ | `getAll()` | `Promise<T[]>` | Get all records |
186
+ | `get(id)` | `Promise<T \| null>` | Get one record by ID |
187
+ | `add(data)` | `Promise<T>` | Create new record (returns with ID) |
188
+ | `put(data)` | `Promise<T>` | Upsert record (requires ID) |
189
+ | `update(id, data)` | `Promise<T \| null>` | Partial update |
190
+ | `delete(id)` | `Promise<boolean>` | Delete record |
191
+ | `filter(fn)` | `Promise<T[]>` | Filter with predicate |
212
192
 
213
- function MyComponent() {
214
- const data = useQuery(db.collection('data').getAll())
193
+ #### Examples
215
194
 
216
- return (
217
- <div>
218
- {
219
- data.map((item: any) => {
220
- <>
221
- // render your data here
222
- </>
223
- })
224
- }
225
- </div>
226
- );
227
- }
228
- ```
229
- Notes:
230
- - must pass in a db function, ie `db.collection('todos').getAll()`
231
- - default will be empty array (this might change in the future)
195
+ ```tsx
196
+ // Create
197
+ const todo = await db.collection('todos').add({
198
+ title: 'Buy milk',
199
+ completed: false
200
+ })
201
+ console.log(todo.id) // Auto-generated ID
232
202
 
203
+ // Read
204
+ const allTodos = await db.collection('todos').getAll()
205
+ const oneTodo = await db.collection('todos').get('some-id')
233
206
 
234
- ### useBasic()
207
+ // Update
208
+ await db.collection('todos').update('some-id', { completed: true })
235
209
 
236
- Returns an object with the following properties and methods:
210
+ // Delete
211
+ await db.collection('todos').delete('some-id')
237
212
 
238
- - `user`: The current user object
239
- - `isSignedIn`: Boolean indicating if the user is signed in
240
- - `signin()`: Function to initiate the sign-in process
241
- - `signout()`: Function to sign out the user
242
- - `signinWithCode(code: string, state?: string)`: Function to manually handle OAuth authorization codes
243
- - `getSignInLink(redirectUri?: string)`: Function to generate OAuth sign-in URLs
244
- - `db`: Object for database operations
213
+ // Filter
214
+ const incomplete = await db.collection('todos').filter(t => !t.completed)
215
+ ```
245
216
 
246
- #### signinWithCode Parameters:
247
- - `code` (required): The OAuth authorization code received from the OAuth provider
248
- - `state` (optional): The state parameter for CSRF protection validation
217
+ ---
249
218
 
250
- #### signinWithCode Returns:
251
- - `Promise<{ success: boolean, error?: string }>`: Returns success status and optional error message
219
+ ## Advanced Usage
252
220
 
253
- #### getSignInLink Parameters:
254
- - `redirectUri` (optional): Custom redirect URI for OAuth flow. If not provided, defaults to current page URL
221
+ ### Manual OAuth Flow
255
222
 
256
- #### getSignInLink Returns:
257
- - `string`: Complete OAuth authorization URL
223
+ For custom OAuth handling (mobile apps, popups, etc.):
258
224
 
225
+ ```tsx
226
+ const { signInWithCode, getSignInUrl } = useBasic()
259
227
 
228
+ // Get OAuth URL with custom redirect
229
+ const url = getSignInUrl('myapp://callback')
260
230
 
261
- db methods:
231
+ // Exchange code for session
232
+ const result = await signInWithCode(code, state)
233
+ if (result.success) {
234
+ console.log('Signed in!')
235
+ }
236
+ ```
262
237
 
263
- - `collection(name: string)`: returns a collection object
238
+ ### Remote Mode
264
239
 
240
+ For server-rendered apps or when you don't need offline support:
265
241
 
266
- db.collection(name) methods:
242
+ ```tsx
243
+ <BasicProvider schema={schema} dbMode="remote">
244
+ <App />
245
+ </BasicProvider>
246
+ ```
267
247
 
268
- - `getAll()`: returns all items in the collection
269
- - `get(id: string)`: returns a single item from the collection
270
- - `add(data: any)`: adds a new item to the collection
271
- - `put(data: any)`: updates an item in the collection
272
- - `update(id: string, data: any)`: updates an item in the collection
273
- - `delete(id: string)`: deletes an item from the collection
248
+ In remote mode:
249
+ - Data is fetched via REST API
250
+ - No IndexedDB storage
251
+ - `useQuery` won't auto-update (use manual refresh)
252
+ - Requires authentication for all operations
274
253
 
275
- all db.collection() methods return a promise
254
+ ### Error Handling
276
255
 
277
- example usage:
256
+ ```tsx
257
+ import { NotAuthenticatedError } from '@basictech/react'
278
258
 
279
- ```typescript
280
- import { useBasic } from '@basictech/react';
259
+ try {
260
+ await db.collection('todos').add({ title: 'Test' })
261
+ } catch (error) {
262
+ if (error instanceof NotAuthenticatedError) {
263
+ // User needs to sign in
264
+ signIn()
265
+ }
266
+ }
267
+ ```
281
268
 
282
- function MyComponent() {
283
- const { db } = useBasic();
269
+ ---
284
270
 
285
- async function addTodo() {
286
- await db.collection('todos').add({
287
- title: 'test',
288
- completed: false
289
- })
290
- }
271
+ ## TypeScript
291
272
 
292
- return (
293
- <div>
294
- <button onClick={addTodo}>Add Todo</button>
295
- </div>
296
- );
273
+ Full TypeScript support with generics:
274
+
275
+ ```tsx
276
+ interface Todo {
277
+ id: string
278
+ title: string
279
+ completed: boolean
280
+ createdAt: number
297
281
  }
298
282
 
283
+ // Type-safe collection
284
+ const todos = db.collection<Todo>('todos')
285
+
286
+ // All methods are typed
287
+ const todo = await todos.add({
288
+ title: 'Test',
289
+ completed: false,
290
+ createdAt: Date.now()
291
+ })
292
+ // todo is typed as Todo
299
293
  ```
300
294
 
295
+ ---
296
+
301
297
  ## License
302
298
 
303
299
  ISC
304
-
305
- ---