@aerostack/sdk-react 0.6.6
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/.tshy/build.json +8 -0
- package/.tshy/commonjs.json +16 -0
- package/.tshy/esm.json +15 -0
- package/README.md +137 -0
- package/examples/README.md +27 -0
- package/examples/basic-auth-app.tsx +64 -0
- package/examples/next-app-router.tsx +55 -0
- package/examples/protected-routes.tsx +38 -0
- package/package.json +65 -0
- package/src/context.tsx +52 -0
- package/src/hooks/useAI.ts +37 -0
- package/src/hooks/useAuth.ts +59 -0
- package/src/hooks/useCache.ts +44 -0
- package/src/hooks/useDb.ts +31 -0
- package/src/hooks/useSubscription.ts +66 -0
- package/src/index.ts +20 -0
- package/tsconfig.json +30 -0
package/.tshy/build.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./build.json",
|
|
3
|
+
"include": [
|
|
4
|
+
"../src/**/*.ts",
|
|
5
|
+
"../src/**/*.cts",
|
|
6
|
+
"../src/**/*.tsx",
|
|
7
|
+
"../src/**/*.json"
|
|
8
|
+
],
|
|
9
|
+
"exclude": [
|
|
10
|
+
"../src/**/*.mts",
|
|
11
|
+
"../src/package.json"
|
|
12
|
+
],
|
|
13
|
+
"compilerOptions": {
|
|
14
|
+
"outDir": "../.tshy-build/commonjs"
|
|
15
|
+
}
|
|
16
|
+
}
|
package/.tshy/esm.json
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# @aerostack/react
|
|
2
|
+
|
|
3
|
+
The official React SDK for Aerostack. Easily integrate authentication, database, AI, and other Aerostack services into your React applications using idiomatic Hooks.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @aerostack/react
|
|
9
|
+
# or
|
|
10
|
+
yarn add @aerostack/react
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @aerostack/react
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### 1. Wrap your app in `AerostackProvider`
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { AerostackProvider } from '@aerostack/react';
|
|
21
|
+
|
|
22
|
+
function App() {
|
|
23
|
+
return (
|
|
24
|
+
<AerostackProvider
|
|
25
|
+
projectUrl="https://your-project.aerostack.dev"
|
|
26
|
+
apiKey="your-public-api-key"
|
|
27
|
+
>
|
|
28
|
+
<YourComponent />
|
|
29
|
+
</AerostackProvider>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 2. Use Hooks
|
|
35
|
+
|
|
36
|
+
#### Authentication
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import { useAuth } from '@aerostack/react';
|
|
40
|
+
|
|
41
|
+
function LoginButton() {
|
|
42
|
+
const { signIn, user, isLoading } = useAuth();
|
|
43
|
+
|
|
44
|
+
if (user) return <div>Welcome, {user.email}</div>;
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<button onClick={() => signIn('email', 'password')}>
|
|
48
|
+
{isLoading ? 'Loading...' : 'Sign In'}
|
|
49
|
+
</button>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
#### Database
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import { useDb } from '@aerostack/react';
|
|
58
|
+
|
|
59
|
+
function TodoList() {
|
|
60
|
+
const { data: todos, isLoading } = useDb('todos').find();
|
|
61
|
+
|
|
62
|
+
if (isLoading) return <div>Loading...</div>;
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<ul>
|
|
66
|
+
{todos.map(todo => <li key={todo.id}>{todo.title}</li>)}
|
|
67
|
+
</ul>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
#### AI Chat
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
import { useAI } from '@aerostack/react';
|
|
76
|
+
|
|
77
|
+
function ChatBot() {
|
|
78
|
+
const { messages, sendMessage } = useAI('support-agent');
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div>
|
|
82
|
+
{messages.map(m => <div>{m.content}</div>)}
|
|
83
|
+
<button onClick={() => sendMessage("Hello!")}>Say Hi</button>
|
|
84
|
+
</div>
|
|
85
|
+
);
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## SSR and Backend Integration
|
|
91
|
+
|
|
92
|
+
### Client-Side Only (SPA)
|
|
93
|
+
This SDK is designed for **client-side React apps**. Hooks like `useAuth()` run in the browser.
|
|
94
|
+
|
|
95
|
+
### Server-Side Rendering (Next.js, Remix)
|
|
96
|
+
For SSR frameworks, use different approaches for server vs. client:
|
|
97
|
+
|
|
98
|
+
**Server-side (data fetching, API routes)**:
|
|
99
|
+
```tsx
|
|
100
|
+
// app/api/users/route.ts (Next.js App Router)
|
|
101
|
+
import { SDK } from '@aerostack/node';
|
|
102
|
+
|
|
103
|
+
export async function GET() {
|
|
104
|
+
const sdk = new SDK({ apiKeyAuth: process.env.AEROSTACK_API_KEY });
|
|
105
|
+
const users = await sdk.database.dbQuery({
|
|
106
|
+
sql: 'SELECT * FROM users'
|
|
107
|
+
});
|
|
108
|
+
return Response.json(users);
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Client-side (React components)**:
|
|
113
|
+
```tsx
|
|
114
|
+
'use client'; // Next.js 13+
|
|
115
|
+
import { AerostackProvider, useAuth } from '@aerostack/react';
|
|
116
|
+
|
|
117
|
+
function ClientComponent() {
|
|
118
|
+
const { user } = useAuth();
|
|
119
|
+
return <div>{user?.email}</div>;
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Backend Worker Pattern
|
|
124
|
+
If building Cloudflare Workers that need both client Auth and server bindings:
|
|
125
|
+
```typescript
|
|
126
|
+
import { AerostackClient, AerostackServer } from '@aerostack/sdk';
|
|
127
|
+
|
|
128
|
+
// Use both SDKs as needed
|
|
129
|
+
const client = new AerostackClient({ projectSlug: "my-project" });
|
|
130
|
+
const server = new AerostackServer(env);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
See [`@aerostack/sdk` documentation](../sdk/README.md#backend-wrapper-pattern) for details.
|
|
134
|
+
|
|
135
|
+
## Documentation
|
|
136
|
+
|
|
137
|
+
For full documentation, visit [docs.aerostack.dev](https://docs.aerostack.dev/sdk/react).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# @aerostack/react Examples
|
|
2
|
+
|
|
3
|
+
Examples for `@aerostack/react`.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @aerostack/react @aerostack/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Available Examples
|
|
12
|
+
|
|
13
|
+
| Example | Description | Pattern |
|
|
14
|
+
|---------|-------------|---------|
|
|
15
|
+
| [**Basic Auth App**](./basic-auth-app.tsx) | Complete Login/Logout flow using `useAuth`. | Client-Side |
|
|
16
|
+
| [**Protected Routes**](./protected-routes.tsx) | Wrapper component for React Router. | Routing |
|
|
17
|
+
| [**Next.js App Router**](./next-app-router.tsx) | Server Components integration pattern. | Next.js 13+ |
|
|
18
|
+
|
|
19
|
+
## Config
|
|
20
|
+
|
|
21
|
+
Wrap your app root:
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
<AerostackProvider projectSlug="my-app">
|
|
25
|
+
<App />
|
|
26
|
+
</AerostackProvider>
|
|
27
|
+
```
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { AerostackProvider, useAuth } from '@aerostack/react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Basic Auth App Example
|
|
6
|
+
*
|
|
7
|
+
* Demonstrates basic usage of AerostackProvider and useAuth hook.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// 1. Root Component wraps app in Provider
|
|
11
|
+
export default function App() {
|
|
12
|
+
return (
|
|
13
|
+
<AerostackProvider
|
|
14
|
+
projectSlug="YOUR_PROJECT_SLUG"
|
|
15
|
+
// baseUrl="https://api.aerostack.ai/v1" // Optional
|
|
16
|
+
>
|
|
17
|
+
<AuthDemo />
|
|
18
|
+
</AerostackProvider>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// 2. Child Component uses hooks
|
|
23
|
+
function AuthDemo() {
|
|
24
|
+
const { user, login, logout, isLoading, error } = useAuth();
|
|
25
|
+
const [email, setEmail] = useState('');
|
|
26
|
+
const [password, setPassword] = useState('');
|
|
27
|
+
|
|
28
|
+
if (isLoading) return <div>Loading...</div>;
|
|
29
|
+
|
|
30
|
+
if (user) {
|
|
31
|
+
return (
|
|
32
|
+
<div>
|
|
33
|
+
<h1>Welcome, {user.name || user.email}!</h1>
|
|
34
|
+
<button onClick={() => logout()}>Logout</button>
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div>
|
|
41
|
+
<h1>Login</h1>
|
|
42
|
+
{error && <div style={{ color: 'red' }}>{error.message}</div>}
|
|
43
|
+
|
|
44
|
+
<form onSubmit={(e) => {
|
|
45
|
+
e.preventDefault();
|
|
46
|
+
login(email, password);
|
|
47
|
+
}}>
|
|
48
|
+
<input
|
|
49
|
+
type="email"
|
|
50
|
+
placeholder="Email"
|
|
51
|
+
value={email}
|
|
52
|
+
onChange={e => setEmail(e.target.value)}
|
|
53
|
+
/>
|
|
54
|
+
<input
|
|
55
|
+
type="password"
|
|
56
|
+
placeholder="Password"
|
|
57
|
+
value={password}
|
|
58
|
+
onChange={e => setPassword(e.target.value)}
|
|
59
|
+
/>
|
|
60
|
+
<button type="submit">Login</button>
|
|
61
|
+
</form>
|
|
62
|
+
</div>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js App Router Example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates how to access user session in Server Components.
|
|
5
|
+
* Note: @aerostack/react is primarily for Client Components.
|
|
6
|
+
* For Server Components, usage depends on where you store the token (cookies vs local storage).
|
|
7
|
+
*
|
|
8
|
+
* If using cookies:
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// app/layout.tsx (Client Component Wrapper)
|
|
12
|
+
/*
|
|
13
|
+
'use client';
|
|
14
|
+
import { AerostackProvider } from '@aerostack/react';
|
|
15
|
+
|
|
16
|
+
export default function RootLayout({ children }) {
|
|
17
|
+
return (
|
|
18
|
+
<html>
|
|
19
|
+
<body>
|
|
20
|
+
<AerostackProvider projectSlug="...">
|
|
21
|
+
{children}
|
|
22
|
+
</AerostackProvider>
|
|
23
|
+
</body>
|
|
24
|
+
</html>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
// app/page.tsx (Server Component)
|
|
30
|
+
import { cookies } from 'next/headers';
|
|
31
|
+
import { AerostackClient } from '@aerostack/sdk'; // Use Core SDK on server
|
|
32
|
+
|
|
33
|
+
async function getUser() {
|
|
34
|
+
const cookieStore = cookies();
|
|
35
|
+
const token = cookieStore.get('auth_token')?.value;
|
|
36
|
+
|
|
37
|
+
if (!token) return null;
|
|
38
|
+
|
|
39
|
+
const client = new AerostackClient({ projectSlug: '...' });
|
|
40
|
+
try {
|
|
41
|
+
return await client.auth.getCurrentUser(token);
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default async function Page() {
|
|
48
|
+
const user = await getUser();
|
|
49
|
+
|
|
50
|
+
if (!user) {
|
|
51
|
+
return <div>Please log in</div>
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return <h1>Hello Server-Side User: {user.email}</h1>
|
|
55
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useAuth } from '@aerostack/react';
|
|
3
|
+
// Assuming React Router usage
|
|
4
|
+
import { Navigate, Outlet } from 'react-router-dom';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Protected Route Example
|
|
8
|
+
*
|
|
9
|
+
* Demonstrates a reusable ProtectedRoute component.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export const ProtectedRoute = () => {
|
|
13
|
+
const { user, isLoading } = useAuth();
|
|
14
|
+
|
|
15
|
+
if (isLoading) {
|
|
16
|
+
return <div>Loading authentication...</div>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!user) {
|
|
20
|
+
// Redirect to login if not authenticated
|
|
21
|
+
return <Navigate to="/login" replace />;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Render child routes if authenticated
|
|
25
|
+
return <Outlet />;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Usage in App.tsx:
|
|
29
|
+
/*
|
|
30
|
+
<Routes>
|
|
31
|
+
<Route path="/login" element={<LoginPage />} />
|
|
32
|
+
|
|
33
|
+
<Route element={<ProtectedRoute />}>
|
|
34
|
+
<Route path="/dashboard" element={<DashboardPage />} />
|
|
35
|
+
<Route path="/profile" element={<ProfilePage />} />
|
|
36
|
+
</Route>
|
|
37
|
+
</Routes>
|
|
38
|
+
*/
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aerostack/sdk-react",
|
|
3
|
+
"version": "0.6.6",
|
|
4
|
+
"author": "Aerostack",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/aerostackdev/sdks.git",
|
|
9
|
+
"directory": "packages/react"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/aerostackdev/sdks/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://docs.aerostack.dev",
|
|
15
|
+
"tshy": {
|
|
16
|
+
"exports": {
|
|
17
|
+
".": "./src/index.ts",
|
|
18
|
+
"./package.json": "./package.json"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tshy",
|
|
24
|
+
"lint": "eslint --cache --max-warnings=0 src",
|
|
25
|
+
"prepublishOnly": "npm run build"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
29
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@eslint/js": "^9.26.0",
|
|
33
|
+
"@types/react": "^18.0.0 || ^19.0.0",
|
|
34
|
+
"@types/react-dom": "^18.0.0 || ^19.0.0",
|
|
35
|
+
"eslint": "^9.26.0",
|
|
36
|
+
"globals": "^15.14.0",
|
|
37
|
+
"react": "^18.3.1",
|
|
38
|
+
"react-dom": "^18.3.1",
|
|
39
|
+
"tshy": "^2.0.0",
|
|
40
|
+
"typescript": "~5.8.3",
|
|
41
|
+
"typescript-eslint": "^8.26.0"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@aerostack/sdk-web": "^0.6.6"
|
|
45
|
+
},
|
|
46
|
+
"exports": {
|
|
47
|
+
".": {
|
|
48
|
+
"import": {
|
|
49
|
+
"types": "./dist/esm/index.d.ts",
|
|
50
|
+
"default": "./dist/esm/index.js"
|
|
51
|
+
},
|
|
52
|
+
"require": {
|
|
53
|
+
"types": "./dist/commonjs/index.d.ts",
|
|
54
|
+
"default": "./dist/commonjs/index.js"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"./package.json": "./package.json"
|
|
58
|
+
},
|
|
59
|
+
"main": "./dist/commonjs/index.js",
|
|
60
|
+
"types": "./dist/commonjs/index.d.ts",
|
|
61
|
+
"module": "./dist/esm/index.js",
|
|
62
|
+
"publishConfig": {
|
|
63
|
+
"access": "public"
|
|
64
|
+
}
|
|
65
|
+
}
|
package/src/context.tsx
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React, { createContext, useContext, useMemo } from 'react';
|
|
2
|
+
import { SDK } from '@aerostack/sdk-web';
|
|
3
|
+
|
|
4
|
+
export interface AerostackContextType {
|
|
5
|
+
sdk: SDK;
|
|
6
|
+
projectId: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const AerostackContext = createContext<AerostackContextType | null>(null);
|
|
10
|
+
|
|
11
|
+
export interface AerostackProviderProps {
|
|
12
|
+
projectId: string;
|
|
13
|
+
baseUrl?: string;
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const AerostackProvider: React.FC<AerostackProviderProps> = ({
|
|
18
|
+
projectId,
|
|
19
|
+
baseUrl = 'https://api.aerostack.ai/v1',
|
|
20
|
+
children,
|
|
21
|
+
}) => {
|
|
22
|
+
const sdk = useMemo(() => {
|
|
23
|
+
// In web context, we use the hook registration for projectId parity
|
|
24
|
+
import('@aerostack/sdk-web').then(mod => {
|
|
25
|
+
// @ts-ignore - the hook logic handles this
|
|
26
|
+
if (mod.setProjectId) mod.setProjectId(projectId);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return new SDK({
|
|
30
|
+
serverURL: baseUrl,
|
|
31
|
+
});
|
|
32
|
+
}, [baseUrl, projectId]);
|
|
33
|
+
|
|
34
|
+
const value = useMemo(() => ({
|
|
35
|
+
sdk,
|
|
36
|
+
projectId,
|
|
37
|
+
}), [sdk, projectId]);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<AerostackContext.Provider value={value}>
|
|
41
|
+
{children}
|
|
42
|
+
</AerostackContext.Provider>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const useAerostack = () => {
|
|
47
|
+
const context = useContext(AerostackContext);
|
|
48
|
+
if (!context) {
|
|
49
|
+
throw new Error('useAerostack must be used within an AerostackProvider');
|
|
50
|
+
}
|
|
51
|
+
return context;
|
|
52
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
import { useAerostack } from '../context.js';
|
|
3
|
+
|
|
4
|
+
export interface ChatMessage {
|
|
5
|
+
role: 'user' | 'assistant' | 'system';
|
|
6
|
+
content: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const useAI = () => {
|
|
10
|
+
const { sdk } = useAerostack();
|
|
11
|
+
const [loading, setLoading] = useState(false);
|
|
12
|
+
const [error, setError] = useState<Error | null>(null);
|
|
13
|
+
|
|
14
|
+
const chat = useCallback(async (messages: ChatMessage[], options?: any) => {
|
|
15
|
+
try {
|
|
16
|
+
setLoading(true);
|
|
17
|
+
setError(null);
|
|
18
|
+
const result = await sdk.ai.aiChat({
|
|
19
|
+
messages,
|
|
20
|
+
...options,
|
|
21
|
+
});
|
|
22
|
+
return result.response;
|
|
23
|
+
} catch (err) {
|
|
24
|
+
const error = err instanceof Error ? err : new Error('AI chat failed');
|
|
25
|
+
setError(error);
|
|
26
|
+
throw error;
|
|
27
|
+
} finally {
|
|
28
|
+
setLoading(false);
|
|
29
|
+
}
|
|
30
|
+
}, [sdk]);
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
chat,
|
|
34
|
+
loading,
|
|
35
|
+
error,
|
|
36
|
+
};
|
|
37
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import { useAerostack } from '../context.js';
|
|
3
|
+
|
|
4
|
+
export interface User {
|
|
5
|
+
id: string;
|
|
6
|
+
email: string;
|
|
7
|
+
name?: string;
|
|
8
|
+
[key: string]: any;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const useAuth = () => {
|
|
12
|
+
const { sdk } = useAerostack();
|
|
13
|
+
const [user, setUser] = useState<User | null>(null);
|
|
14
|
+
const [loading, setLoading] = useState(true);
|
|
15
|
+
const [error, setError] = useState<Error | null>(null);
|
|
16
|
+
|
|
17
|
+
const refreshUser = useCallback(async () => {
|
|
18
|
+
try {
|
|
19
|
+
setLoading(true);
|
|
20
|
+
// Logic to fetch current user session from sdk.authentication
|
|
21
|
+
// const session = await sdk.authentication.getSession();
|
|
22
|
+
// setUser(session.user);
|
|
23
|
+
} catch (err) {
|
|
24
|
+
setError(err instanceof Error ? err : new Error('Failed to fetch user'));
|
|
25
|
+
} finally {
|
|
26
|
+
setLoading(false);
|
|
27
|
+
}
|
|
28
|
+
}, [sdk]);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
refreshUser();
|
|
32
|
+
}, [refreshUser]);
|
|
33
|
+
|
|
34
|
+
const signIn = async (credentials: any) => {
|
|
35
|
+
const result = await sdk.authentication.authSignin(credentials);
|
|
36
|
+
// setUser(result.user);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const signUp = async (credentials: any) => {
|
|
41
|
+
const result = await sdk.authentication.authSignup(credentials);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const signOut = async () => {
|
|
46
|
+
// await sdk.authentication.signOut();
|
|
47
|
+
setUser(null);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
user,
|
|
52
|
+
loading,
|
|
53
|
+
error,
|
|
54
|
+
signIn,
|
|
55
|
+
signUp,
|
|
56
|
+
signOut,
|
|
57
|
+
refreshUser,
|
|
58
|
+
};
|
|
59
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
import { useAerostack } from '../context.js';
|
|
3
|
+
|
|
4
|
+
export const useCache = () => {
|
|
5
|
+
const { sdk } = useAerostack();
|
|
6
|
+
const [loading, setLoading] = useState(false);
|
|
7
|
+
const [error, setError] = useState<Error | null>(null);
|
|
8
|
+
|
|
9
|
+
const get = useCallback(async (key: string) => {
|
|
10
|
+
try {
|
|
11
|
+
setLoading(true);
|
|
12
|
+
setError(null);
|
|
13
|
+
const result = await sdk.cache.cacheGet({ key });
|
|
14
|
+
return result.value;
|
|
15
|
+
} catch (err) {
|
|
16
|
+
const error = err instanceof Error ? err : new Error('Cache get failed');
|
|
17
|
+
setError(error);
|
|
18
|
+
throw error;
|
|
19
|
+
} finally {
|
|
20
|
+
setLoading(false);
|
|
21
|
+
}
|
|
22
|
+
}, [sdk]);
|
|
23
|
+
|
|
24
|
+
const set = useCallback(async (key: string, value: any, ttl?: number) => {
|
|
25
|
+
try {
|
|
26
|
+
setLoading(true);
|
|
27
|
+
setError(null);
|
|
28
|
+
await sdk.cache.cacheSet({ key, value, ttl });
|
|
29
|
+
} catch (err) {
|
|
30
|
+
const error = err instanceof Error ? err : new Error('Cache set failed');
|
|
31
|
+
setError(error);
|
|
32
|
+
throw error;
|
|
33
|
+
} finally {
|
|
34
|
+
setLoading(false);
|
|
35
|
+
}
|
|
36
|
+
}, [sdk]);
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
get,
|
|
40
|
+
set,
|
|
41
|
+
loading,
|
|
42
|
+
error,
|
|
43
|
+
};
|
|
44
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
import { useAerostack } from '../context.js';
|
|
3
|
+
|
|
4
|
+
export const useDb = () => {
|
|
5
|
+
const { sdk } = useAerostack();
|
|
6
|
+
const [loading, setLoading] = useState(false);
|
|
7
|
+
const [error, setError] = useState<Error | null>(null);
|
|
8
|
+
|
|
9
|
+
const query = useCallback(async (sql: string, params?: any[]) => {
|
|
10
|
+
try {
|
|
11
|
+
setLoading(true);
|
|
12
|
+
setError(null);
|
|
13
|
+
const result = await sdk.database.dbQuery({
|
|
14
|
+
requestBody: { sql, params }
|
|
15
|
+
});
|
|
16
|
+
return result; // result is already DBQueryResult
|
|
17
|
+
} catch (err) {
|
|
18
|
+
const error = err instanceof Error ? err : new Error('Database query failed');
|
|
19
|
+
setError(error);
|
|
20
|
+
throw error;
|
|
21
|
+
} finally {
|
|
22
|
+
setLoading(false);
|
|
23
|
+
}
|
|
24
|
+
}, [sdk]);
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
query,
|
|
28
|
+
loading,
|
|
29
|
+
error,
|
|
30
|
+
};
|
|
31
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { useAerostack } from '../context.js';
|
|
3
|
+
export interface RealtimeSubscriptionOptions {
|
|
4
|
+
event?: 'INSERT' | 'UPDATE' | 'DELETE' | '*';
|
|
5
|
+
filter?: Record<string, any>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Hook to subscribe to Aerostack realtime events
|
|
10
|
+
*
|
|
11
|
+
* @param topic The topic to subscribe to (e.g., 'posts' or 'table/posts/projectId')
|
|
12
|
+
* @param options Event type and filtering options
|
|
13
|
+
*/
|
|
14
|
+
export function useSubscription<T = any>(
|
|
15
|
+
topic: string,
|
|
16
|
+
options: RealtimeSubscriptionOptions = {}
|
|
17
|
+
) {
|
|
18
|
+
const { sdk } = useAerostack();
|
|
19
|
+
const [data, setData] = useState<T | null>(null);
|
|
20
|
+
const [error, setError] = useState<Error | null>(null);
|
|
21
|
+
const [loading, setLoading] = useState(true);
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
let isMounted = true;
|
|
25
|
+
const realtime = (sdk as any).realtime;
|
|
26
|
+
|
|
27
|
+
if (!realtime) {
|
|
28
|
+
setError(new Error('Realtime not supported in this SDK version'));
|
|
29
|
+
setLoading(false);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const channel = realtime.channel(topic, options);
|
|
34
|
+
|
|
35
|
+
const setup = async () => {
|
|
36
|
+
try {
|
|
37
|
+
await realtime.connect();
|
|
38
|
+
|
|
39
|
+
channel.on('*', (payload: any) => {
|
|
40
|
+
if (isMounted) {
|
|
41
|
+
setData(payload);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
channel.subscribe();
|
|
46
|
+
setLoading(false);
|
|
47
|
+
} catch (err: any) {
|
|
48
|
+
if (isMounted) {
|
|
49
|
+
setError(err);
|
|
50
|
+
setLoading(false);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
setup();
|
|
56
|
+
|
|
57
|
+
return () => {
|
|
58
|
+
isMounted = false;
|
|
59
|
+
// Note: We don't automatically disconnect the global client,
|
|
60
|
+
// but we could unsubscribe from the channel if needed.
|
|
61
|
+
// channel.unsubscribe();
|
|
62
|
+
};
|
|
63
|
+
}, [sdk, topic, JSON.stringify(options)]);
|
|
64
|
+
|
|
65
|
+
return { data, error, loading };
|
|
66
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export * from './context.js';
|
|
2
|
+
export * from './hooks/useAuth.js';
|
|
3
|
+
export * from './hooks/useDb.js';
|
|
4
|
+
export * from './hooks/useAI.js';
|
|
5
|
+
export * from './hooks/useCache.js';
|
|
6
|
+
|
|
7
|
+
// Re-export core types and client for convenience
|
|
8
|
+
import { SDK, SDKOptions } from '@aerostack/sdk-web';
|
|
9
|
+
|
|
10
|
+
export type { SDK };
|
|
11
|
+
export const AerostackClient = SDK;
|
|
12
|
+
export type AerostackClient = SDK;
|
|
13
|
+
export type AerostackConfig = SDKOptions;
|
|
14
|
+
|
|
15
|
+
/** @deprecated Use AerostackClient instead. */
|
|
16
|
+
export const AerocallClient = SDK;
|
|
17
|
+
/** @deprecated Use AerostackClient instead. */
|
|
18
|
+
export type AerocallClient = SDK;
|
|
19
|
+
/** @deprecated Use AerostackConfig instead. */
|
|
20
|
+
export type AerocallConfig = SDKOptions;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"incremental": false,
|
|
4
|
+
"target": "ES2020",
|
|
5
|
+
"lib": [
|
|
6
|
+
"ES2022",
|
|
7
|
+
"DOM",
|
|
8
|
+
"DOM.Iterable"
|
|
9
|
+
],
|
|
10
|
+
"jsx": "react-jsx",
|
|
11
|
+
"module": "Node16",
|
|
12
|
+
"moduleResolution": "Node16",
|
|
13
|
+
"allowJs": true,
|
|
14
|
+
"declaration": true,
|
|
15
|
+
"declarationMap": true,
|
|
16
|
+
"sourceMap": true,
|
|
17
|
+
"outDir": ".",
|
|
18
|
+
"strict": true,
|
|
19
|
+
"isolatedModules": true,
|
|
20
|
+
"esModuleInterop": true,
|
|
21
|
+
"skipLibCheck": true,
|
|
22
|
+
"forceConsistentCasingInFileNames": true
|
|
23
|
+
},
|
|
24
|
+
"include": [
|
|
25
|
+
"src"
|
|
26
|
+
],
|
|
27
|
+
"exclude": [
|
|
28
|
+
"node_modules"
|
|
29
|
+
]
|
|
30
|
+
}
|