corebase-js 0.1.0 → 0.2.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 +209 -24
- package/dist/auth.d.ts +5 -13
- package/dist/client.d.ts +6 -0
- package/dist/client.js +8 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/query.d.ts +42 -0
- package/dist/query.js +141 -0
- package/dist/react.d.ts +21 -0
- package/dist/react.js +57 -0
- package/dist/realtime.d.ts +28 -0
- package/dist/realtime.js +122 -0
- package/dist/storage.d.ts +19 -0
- package/dist/storage.js +77 -0
- package/dist/types.d.ts +10 -1
- package/package.json +15 -1
package/README.md
CHANGED
|
@@ -1,69 +1,254 @@
|
|
|
1
1
|
# CoreBase JS Client
|
|
2
2
|
|
|
3
|
-
A JavaScript/TypeScript client for CoreBase.
|
|
3
|
+
A robust JavaScript/TypeScript client for communicating with your CoreBase backend. This library provides a simple, Supabase-like interface for authentication and database operations.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install corebase-js
|
|
9
|
+
|
|
10
|
+
# If using React/React Native hooks (Optioanal peer dependency)
|
|
11
|
+
npm install --save-dev react @types/react
|
|
9
12
|
```
|
|
10
13
|
|
|
11
14
|
## Initialization
|
|
12
15
|
|
|
13
|
-
Initialize the client with your project URL and API Key.
|
|
16
|
+
Initialize the client with your CoreBase project URL and API Key.
|
|
14
17
|
|
|
15
|
-
```
|
|
18
|
+
```typescript
|
|
16
19
|
import { createClient } from 'corebase-js';
|
|
17
20
|
|
|
18
|
-
const
|
|
21
|
+
const CLIENT_URL = 'https://corebase.trivyaa.in'; // Your CoreBase API URL
|
|
22
|
+
const API_KEY = 'pk_...'; // Your Project Public API Key
|
|
23
|
+
|
|
24
|
+
const corebase = createClient(CLIENT_URL, API_KEY);
|
|
19
25
|
```
|
|
20
26
|
|
|
21
27
|
## Authentication
|
|
22
28
|
|
|
29
|
+
Manage user sessions securely.
|
|
30
|
+
|
|
23
31
|
### Sign Up
|
|
24
32
|
|
|
25
|
-
|
|
33
|
+
Register a new user in your project.
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
26
36
|
const { data, error } = await corebase.auth.signUp({
|
|
27
37
|
email: 'user@example.com',
|
|
28
|
-
password: '
|
|
38
|
+
password: 'securePassword123',
|
|
29
39
|
name: 'John Doe'
|
|
30
40
|
});
|
|
31
41
|
|
|
32
|
-
if (error)
|
|
33
|
-
|
|
34
|
-
} else {
|
|
35
|
-
console.log('User created:', data.user);
|
|
36
|
-
}
|
|
42
|
+
if (error) console.error('Signup error:', error);
|
|
43
|
+
else console.log('Welcome!', data.user);
|
|
37
44
|
```
|
|
38
45
|
|
|
39
46
|
### Sign In
|
|
40
47
|
|
|
41
|
-
|
|
48
|
+
Log in an existing user. The session is automatically persisted (in localStorage for browsers).
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
42
51
|
const { data, error } = await corebase.auth.signIn({
|
|
43
52
|
email: 'user@example.com',
|
|
44
|
-
password: '
|
|
53
|
+
password: 'securePassword123'
|
|
45
54
|
});
|
|
46
55
|
|
|
47
|
-
if (error)
|
|
48
|
-
|
|
49
|
-
} else {
|
|
50
|
-
console.log('Logged in user:', data.user);
|
|
51
|
-
console.log('Access token:', data.access_token);
|
|
52
|
-
}
|
|
56
|
+
if (error) console.error('Login error:', error);
|
|
57
|
+
else console.log('Logged in:', data.user);
|
|
53
58
|
```
|
|
54
59
|
|
|
55
60
|
### Get Current User
|
|
56
61
|
|
|
57
|
-
|
|
62
|
+
Retrieve the currently authenticated user's details.
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
58
65
|
const { data, error } = await corebase.auth.getUser();
|
|
59
66
|
|
|
60
|
-
if (data)
|
|
61
|
-
console.log('Current user:', data.user);
|
|
62
|
-
}
|
|
67
|
+
if (data) console.log('Current user:', data.user);
|
|
63
68
|
```
|
|
64
69
|
|
|
65
70
|
### Sign Out
|
|
66
71
|
|
|
67
|
-
|
|
72
|
+
Clear the current session.
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
68
75
|
await corebase.auth.signOut();
|
|
69
76
|
```
|
|
77
|
+
|
|
78
|
+
## specific Database Operations
|
|
79
|
+
|
|
80
|
+
Perform CRUD operations on your tables using a fluent, chainable API.
|
|
81
|
+
|
|
82
|
+
### Select Data (Read)
|
|
83
|
+
|
|
84
|
+
**Get all rows:**
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
const { data, error, count } = await corebase
|
|
88
|
+
.from('posts')
|
|
89
|
+
.select('*');
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Select specific columns:**
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
const { data } = await corebase
|
|
96
|
+
.from('users')
|
|
97
|
+
.select('id, name, email');
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Filtering (Where clause):**
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
const { data } = await corebase
|
|
104
|
+
.from('posts')
|
|
105
|
+
.select('*')
|
|
106
|
+
.eq('status', 'published')
|
|
107
|
+
.match({ author_id: 'user_123', category: 'tech' });
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Pagination & Sorting:**
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
const { data, count } = await corebase
|
|
114
|
+
.from('posts')
|
|
115
|
+
.select('*')
|
|
116
|
+
.order('created_at', { ascending: false }) // Sort by newest
|
|
117
|
+
.limit(10) // Take 10
|
|
118
|
+
.page(1); // Page 1
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Get a Single Record:**
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
const { data: user, error } = await corebase
|
|
125
|
+
.from('users')
|
|
126
|
+
.select('*')
|
|
127
|
+
.eq('id', 1)
|
|
128
|
+
.single();
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Insert Data (Create)
|
|
132
|
+
|
|
133
|
+
Insert one or multiple rows.
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
// Single insert
|
|
137
|
+
const { data, error } = await corebase
|
|
138
|
+
.from('posts')
|
|
139
|
+
.insert({
|
|
140
|
+
title: 'My New Post',
|
|
141
|
+
content: 'Hello World',
|
|
142
|
+
user_id: 'user_123'
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Bulk insert
|
|
146
|
+
const { data, error } = await corebase
|
|
147
|
+
.from('posts')
|
|
148
|
+
.insert([
|
|
149
|
+
{ title: 'Post 1', user_id: 'user_123' },
|
|
150
|
+
{ title: 'Post 2', user_id: 'user_123' }
|
|
151
|
+
]);
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Update Data
|
|
155
|
+
|
|
156
|
+
Update rows matching specific criteria.
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
const { data, error } = await corebase
|
|
160
|
+
.from('posts')
|
|
161
|
+
.update({ status: 'archived' })
|
|
162
|
+
.eq('status', 'draft')
|
|
163
|
+
.match({ user_id: 'user_123' });
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Delete Data
|
|
167
|
+
|
|
168
|
+
Delete rows matching specific criteria.
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
const { data, error } = await corebase
|
|
172
|
+
.from('posts')
|
|
173
|
+
.delete()
|
|
174
|
+
.eq('id', 123);
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## File Storage
|
|
178
|
+
|
|
179
|
+
Upload files to your project buckets.
|
|
180
|
+
|
|
181
|
+
### Upload File
|
|
182
|
+
|
|
183
|
+
Upload a file directly from the browser (e.g., from an `<input type="file" />`).
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
// Assuming you have an input element: <input type="file" id="fileInput" />
|
|
187
|
+
const fileInput = document.getElementById('fileInput') as HTMLInputElement;
|
|
188
|
+
const file = fileInput.files?.[0];
|
|
189
|
+
|
|
190
|
+
if (file) {
|
|
191
|
+
const { data, error } = await corebase.storage.upload(file);
|
|
192
|
+
|
|
193
|
+
if (error) {
|
|
194
|
+
console.error('Upload failed:', error);
|
|
195
|
+
} else {
|
|
196
|
+
console.log('File uploaded successfully!');
|
|
197
|
+
console.log('File Key:', data.key);
|
|
198
|
+
console.log('Upload URL:', data.url);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Realtime Data (React / React Native)
|
|
204
|
+
|
|
205
|
+
Subscribe to live data changes using the `useQuery` hook.
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
import { useQuery } from 'corebase-js/react';
|
|
209
|
+
|
|
210
|
+
// Define your query
|
|
211
|
+
const query = {
|
|
212
|
+
from: 'posts',
|
|
213
|
+
select: ['id', 'title', 'likes'],
|
|
214
|
+
where: {
|
|
215
|
+
status: 'published'
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const MyComponent = () => {
|
|
220
|
+
// Subscribe to realtime updates
|
|
221
|
+
const { data, loading, error } = useQuery(corebase, query);
|
|
222
|
+
|
|
223
|
+
if (loading) return <div>Loading...</div>;
|
|
224
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
225
|
+
|
|
226
|
+
return (
|
|
227
|
+
<ul>
|
|
228
|
+
{data.map(post => (
|
|
229
|
+
<li key={post.id}>
|
|
230
|
+
{post.title} ({post.likes} likes)
|
|
231
|
+
</li>
|
|
232
|
+
))}
|
|
233
|
+
</ul>
|
|
234
|
+
);
|
|
235
|
+
};
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## TypeScript Support
|
|
239
|
+
|
|
240
|
+
This library is written in TypeScript and exports types for all responses.
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
import { User, AuthSession, ClientResponse } from 'corebase-js';
|
|
244
|
+
|
|
245
|
+
// You can also provide a generic type to .from() for typed results
|
|
246
|
+
interface Post {
|
|
247
|
+
id: number;
|
|
248
|
+
title: string;
|
|
249
|
+
content: string;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const { data } = await corebase.from<Post>('posts').select('*');
|
|
253
|
+
// data is typed as Post[] | null
|
|
254
|
+
```
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
import { AuthSession, ClientConfig, User } from './types';
|
|
2
|
-
export interface AuthResponse<T> {
|
|
3
|
-
data: T | null;
|
|
4
|
-
error: {
|
|
5
|
-
message: string;
|
|
6
|
-
code?: string;
|
|
7
|
-
details?: string;
|
|
8
|
-
} | null;
|
|
9
|
-
}
|
|
1
|
+
import { AuthSession, ClientConfig, ClientResponse, User } from './types';
|
|
10
2
|
export declare class AuthClient {
|
|
11
3
|
private config;
|
|
12
4
|
private session;
|
|
@@ -14,19 +6,19 @@ export declare class AuthClient {
|
|
|
14
6
|
constructor(config: ClientConfig);
|
|
15
7
|
private get baseUrl();
|
|
16
8
|
private get headers();
|
|
17
|
-
|
|
9
|
+
request<T>(path: string, options?: RequestInit): Promise<ClientResponse<T>>;
|
|
18
10
|
signUp(credentials: {
|
|
19
11
|
email: string;
|
|
20
12
|
password: string;
|
|
21
13
|
name?: string;
|
|
22
|
-
}): Promise<
|
|
14
|
+
}): Promise<ClientResponse<{
|
|
23
15
|
user: User;
|
|
24
16
|
}>>;
|
|
25
17
|
signIn(credentials: {
|
|
26
18
|
email: string;
|
|
27
19
|
password: string;
|
|
28
|
-
}): Promise<
|
|
29
|
-
getUser(): Promise<
|
|
20
|
+
}): Promise<ClientResponse<AuthSession>>;
|
|
21
|
+
getUser(): Promise<ClientResponse<{
|
|
30
22
|
user: User;
|
|
31
23
|
}>>;
|
|
32
24
|
signOut(): Promise<{
|
package/dist/client.d.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { AuthClient } from './auth';
|
|
2
|
+
import { QueryBuilder } from './query';
|
|
3
|
+
import { StorageClient } from './storage';
|
|
4
|
+
import { RealtimeClient } from './realtime';
|
|
2
5
|
import { ClientConfig } from './types';
|
|
3
6
|
export declare class CoreBaseClient {
|
|
4
7
|
auth: AuthClient;
|
|
8
|
+
storage: StorageClient;
|
|
9
|
+
realtime: RealtimeClient;
|
|
5
10
|
private config;
|
|
6
11
|
constructor(config: ClientConfig);
|
|
12
|
+
from<T = any>(table: string): QueryBuilder<T>;
|
|
7
13
|
}
|
|
8
14
|
export declare function createClient(baseUrl: string, apiKey: string): CoreBaseClient;
|
package/dist/client.js
CHANGED
|
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.CoreBaseClient = void 0;
|
|
4
4
|
exports.createClient = createClient;
|
|
5
5
|
const auth_1 = require("./auth");
|
|
6
|
+
const query_1 = require("./query");
|
|
7
|
+
const storage_1 = require("./storage");
|
|
8
|
+
const realtime_1 = require("./realtime");
|
|
6
9
|
class CoreBaseClient {
|
|
7
10
|
constructor(config) {
|
|
8
11
|
if (!config.apiKey) {
|
|
@@ -13,6 +16,11 @@ class CoreBaseClient {
|
|
|
13
16
|
}
|
|
14
17
|
this.config = config;
|
|
15
18
|
this.auth = new auth_1.AuthClient(config);
|
|
19
|
+
this.storage = new storage_1.StorageClient(this.auth);
|
|
20
|
+
this.realtime = new realtime_1.RealtimeClient(config, this.auth);
|
|
21
|
+
}
|
|
22
|
+
from(table) {
|
|
23
|
+
return new query_1.QueryBuilder((path, options) => this.auth.request(path, options), table);
|
|
16
24
|
}
|
|
17
25
|
}
|
|
18
26
|
exports.CoreBaseClient = CoreBaseClient;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -17,3 +17,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./types"), exports);
|
|
18
18
|
__exportStar(require("./client"), exports);
|
|
19
19
|
__exportStar(require("./auth"), exports);
|
|
20
|
+
__exportStar(require("./query"), exports);
|
|
21
|
+
__exportStar(require("./storage"), exports);
|
|
22
|
+
__exportStar(require("./realtime"), exports);
|
package/dist/query.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ClientResponse } from './types';
|
|
2
|
+
export type RequestHandler = <T>(path: string, options?: RequestInit) => Promise<ClientResponse<T>>;
|
|
3
|
+
export declare class QueryBuilder<T = any> {
|
|
4
|
+
private request;
|
|
5
|
+
private tableName;
|
|
6
|
+
constructor(request: RequestHandler, tableName: string);
|
|
7
|
+
select(columns?: string): SelectBuilder<T>;
|
|
8
|
+
insert(values: Partial<T> | Partial<T>[]): Promise<ClientResponse<{
|
|
9
|
+
message: string;
|
|
10
|
+
count: number;
|
|
11
|
+
}>>;
|
|
12
|
+
update(values: Partial<T>): FilterBuilder<any>;
|
|
13
|
+
delete(): FilterBuilder<any>;
|
|
14
|
+
}
|
|
15
|
+
export declare class FilterBuilder<T> implements PromiseLike<ClientResponse<T>> {
|
|
16
|
+
protected request: RequestHandler;
|
|
17
|
+
protected tableName: string;
|
|
18
|
+
protected method: 'UPDATE' | 'DELETE' | 'SELECT';
|
|
19
|
+
protected body?: any | undefined;
|
|
20
|
+
protected columns?: string | undefined;
|
|
21
|
+
protected filters: Record<string, any>;
|
|
22
|
+
constructor(request: RequestHandler, tableName: string, method: 'UPDATE' | 'DELETE' | 'SELECT', body?: any | undefined, columns?: string | undefined);
|
|
23
|
+
eq(column: string, value: any): this;
|
|
24
|
+
match(query: Record<string, any>): this;
|
|
25
|
+
then<TResult1 = ClientResponse<T>, TResult2 = never>(onfulfilled?: ((value: ClientResponse<T>) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): PromiseLike<TResult1 | TResult2>;
|
|
26
|
+
execute(): Promise<ClientResponse<T>>;
|
|
27
|
+
}
|
|
28
|
+
export declare class SelectBuilder<T> extends FilterBuilder<T> {
|
|
29
|
+
private _limit?;
|
|
30
|
+
private _page?;
|
|
31
|
+
private _sort?;
|
|
32
|
+
private _order?;
|
|
33
|
+
private _isSingle;
|
|
34
|
+
constructor(request: RequestHandler, tableName: string, columns: string);
|
|
35
|
+
limit(count: number): this;
|
|
36
|
+
page(page: number): this;
|
|
37
|
+
order(column: string, { ascending }?: {
|
|
38
|
+
ascending?: boolean;
|
|
39
|
+
}): this;
|
|
40
|
+
single(): this;
|
|
41
|
+
execute(): Promise<ClientResponse<T>>;
|
|
42
|
+
}
|
package/dist/query.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.SelectBuilder = exports.FilterBuilder = exports.QueryBuilder = void 0;
|
|
13
|
+
class QueryBuilder {
|
|
14
|
+
constructor(request, tableName) {
|
|
15
|
+
this.request = request;
|
|
16
|
+
this.tableName = tableName;
|
|
17
|
+
}
|
|
18
|
+
select(columns = '*') {
|
|
19
|
+
return new SelectBuilder(this.request, this.tableName, columns);
|
|
20
|
+
}
|
|
21
|
+
insert(values) {
|
|
22
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
return this.request(`/table_operation/insert/${encodeURIComponent(this.tableName)}`, {
|
|
24
|
+
method: 'POST',
|
|
25
|
+
body: JSON.stringify(values),
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
update(values) {
|
|
30
|
+
return new FilterBuilder(this.request, this.tableName, 'UPDATE', values);
|
|
31
|
+
}
|
|
32
|
+
delete() {
|
|
33
|
+
return new FilterBuilder(this.request, this.tableName, 'DELETE');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.QueryBuilder = QueryBuilder;
|
|
37
|
+
class FilterBuilder {
|
|
38
|
+
constructor(request, tableName, method, body, columns) {
|
|
39
|
+
this.request = request;
|
|
40
|
+
this.tableName = tableName;
|
|
41
|
+
this.method = method;
|
|
42
|
+
this.body = body;
|
|
43
|
+
this.columns = columns;
|
|
44
|
+
this.filters = {};
|
|
45
|
+
}
|
|
46
|
+
eq(column, value) {
|
|
47
|
+
this.filters[column] = value;
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
match(query) {
|
|
51
|
+
Object.assign(this.filters, query);
|
|
52
|
+
return this;
|
|
53
|
+
}
|
|
54
|
+
then(onfulfilled, onrejected) {
|
|
55
|
+
return this.execute().then(onfulfilled, onrejected);
|
|
56
|
+
}
|
|
57
|
+
execute() {
|
|
58
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
if (this.method === 'SELECT') {
|
|
60
|
+
throw new Error("Execute should be called on SelectBuilder");
|
|
61
|
+
}
|
|
62
|
+
const path = `/table_operation/${this.method.toLowerCase()}/${encodeURIComponent(this.tableName)}`;
|
|
63
|
+
let payload = {};
|
|
64
|
+
if (this.method === 'UPDATE') {
|
|
65
|
+
payload = {
|
|
66
|
+
updates: this.body,
|
|
67
|
+
where: this.filters
|
|
68
|
+
};
|
|
69
|
+
return this.request(path, { method: 'PUT', body: JSON.stringify(payload) });
|
|
70
|
+
}
|
|
71
|
+
else if (this.method === 'DELETE') {
|
|
72
|
+
payload = {
|
|
73
|
+
where: this.filters
|
|
74
|
+
};
|
|
75
|
+
return this.request(path, { method: 'DELETE', body: JSON.stringify(payload) });
|
|
76
|
+
}
|
|
77
|
+
return { data: null, error: { message: 'Invalid method' } };
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.FilterBuilder = FilterBuilder;
|
|
82
|
+
class SelectBuilder extends FilterBuilder {
|
|
83
|
+
constructor(request, tableName, columns) {
|
|
84
|
+
super(request, tableName, 'SELECT', undefined, columns);
|
|
85
|
+
this._isSingle = false;
|
|
86
|
+
}
|
|
87
|
+
limit(count) {
|
|
88
|
+
this._limit = count;
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
page(page) {
|
|
92
|
+
this._page = page;
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
order(column, { ascending = true } = {}) {
|
|
96
|
+
this._sort = column;
|
|
97
|
+
this._order = ascending ? 'ASC' : 'DESC';
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
single() {
|
|
101
|
+
this._limit = 1;
|
|
102
|
+
this._isSingle = true;
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
105
|
+
execute() {
|
|
106
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
107
|
+
var _a, _b, _c;
|
|
108
|
+
const payload = {
|
|
109
|
+
columns: this.columns === '*' ? undefined : (_a = this.columns) === null || _a === void 0 ? void 0 : _a.split(',').map(s => s.trim()),
|
|
110
|
+
where: Object.keys(this.filters).length > 0 ? this.filters : undefined,
|
|
111
|
+
limit: this._limit,
|
|
112
|
+
page: this._page,
|
|
113
|
+
sort: this._sort,
|
|
114
|
+
order: this._order
|
|
115
|
+
};
|
|
116
|
+
const response = yield this.request(`/table_operation/select/${encodeURIComponent(this.tableName)}`, {
|
|
117
|
+
method: 'POST',
|
|
118
|
+
body: JSON.stringify(payload)
|
|
119
|
+
});
|
|
120
|
+
if (response.error)
|
|
121
|
+
return { data: null, error: response.error };
|
|
122
|
+
const resultData = (_b = response.data) === null || _b === void 0 ? void 0 : _b.data; // Array of items
|
|
123
|
+
const meta = (_c = response.data) === null || _c === void 0 ? void 0 : _c.meta;
|
|
124
|
+
let finalData = resultData;
|
|
125
|
+
if (this._isSingle) {
|
|
126
|
+
if (Array.isArray(resultData) && resultData.length > 0) {
|
|
127
|
+
finalData = resultData[0];
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
finalData = null; // Or undefined? Supabase errors here usually.
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
data: finalData,
|
|
135
|
+
error: null,
|
|
136
|
+
count: meta === null || meta === void 0 ? void 0 : meta.total
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
exports.SelectBuilder = SelectBuilder;
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { CoreBaseClient } from './client';
|
|
2
|
+
export interface UseQueryOptions {
|
|
3
|
+
enabled?: boolean;
|
|
4
|
+
onData?: (data: any) => void;
|
|
5
|
+
onError?: (error: any) => void;
|
|
6
|
+
}
|
|
7
|
+
export interface UseQueryResult<T> {
|
|
8
|
+
data: T | null;
|
|
9
|
+
loading: boolean;
|
|
10
|
+
error: any;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Hook to subscribe to realtime data queries.
|
|
14
|
+
* Compatible with React and React Native.
|
|
15
|
+
*
|
|
16
|
+
* @param client - The CoreBaseClient instance
|
|
17
|
+
* @param query - The query object definition
|
|
18
|
+
* @param options - Options for the hook
|
|
19
|
+
* @returns The current data, loading state, and error
|
|
20
|
+
*/
|
|
21
|
+
export declare function useQuery<T = any>(client: CoreBaseClient, query: any, options?: UseQueryOptions): UseQueryResult<T>;
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useQuery = useQuery;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
/**
|
|
6
|
+
* Hook to subscribe to realtime data queries.
|
|
7
|
+
* Compatible with React and React Native.
|
|
8
|
+
*
|
|
9
|
+
* @param client - The CoreBaseClient instance
|
|
10
|
+
* @param query - The query object definition
|
|
11
|
+
* @param options - Options for the hook
|
|
12
|
+
* @returns The current data, loading state, and error
|
|
13
|
+
*/
|
|
14
|
+
function useQuery(client, query, options = {}) {
|
|
15
|
+
const [data, setData] = (0, react_1.useState)(null);
|
|
16
|
+
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
17
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
18
|
+
const { enabled = true, onData, onError } = options;
|
|
19
|
+
// Use stringified query to detect changes deeply
|
|
20
|
+
const queryJson = JSON.stringify(query);
|
|
21
|
+
(0, react_1.useEffect)(() => {
|
|
22
|
+
if (!enabled) {
|
|
23
|
+
setLoading(false);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
let subId;
|
|
27
|
+
const subscribe = () => {
|
|
28
|
+
setLoading(true);
|
|
29
|
+
setError(null);
|
|
30
|
+
try {
|
|
31
|
+
if (!client.realtime) {
|
|
32
|
+
throw new Error("RealtimeClient is not initialized on the CoreBaseClient instance.");
|
|
33
|
+
}
|
|
34
|
+
subId = client.realtime.subscribe(query, (newData) => {
|
|
35
|
+
setData(newData);
|
|
36
|
+
setLoading(false);
|
|
37
|
+
if (onData)
|
|
38
|
+
onData(newData);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
console.error("CoreBase useQuery error:", err);
|
|
43
|
+
setError(err);
|
|
44
|
+
setLoading(false);
|
|
45
|
+
if (onError)
|
|
46
|
+
onError(err);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
subscribe();
|
|
50
|
+
return () => {
|
|
51
|
+
if (subId && client.realtime) {
|
|
52
|
+
client.realtime.unsubscribe(subId);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}, [client, queryJson, enabled]); // Re-run if client, query or enabled status changes
|
|
56
|
+
return { data, loading, error };
|
|
57
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AuthClient } from './auth';
|
|
2
|
+
import { ClientConfig } from './types';
|
|
3
|
+
export interface RealtimeMessage {
|
|
4
|
+
type: 'subscribe' | 'unsubscribe' | 'data' | 'error';
|
|
5
|
+
id?: string;
|
|
6
|
+
query?: any;
|
|
7
|
+
data?: any;
|
|
8
|
+
error?: any;
|
|
9
|
+
}
|
|
10
|
+
export type RealtimeCallback = (data: any) => void;
|
|
11
|
+
export declare class RealtimeClient {
|
|
12
|
+
private ws;
|
|
13
|
+
private subscriptions;
|
|
14
|
+
private reconnectTimer;
|
|
15
|
+
private config;
|
|
16
|
+
private auth;
|
|
17
|
+
private isConnected;
|
|
18
|
+
constructor(config: ClientConfig, auth: AuthClient);
|
|
19
|
+
private getUrl;
|
|
20
|
+
connect(): void;
|
|
21
|
+
disconnect(): void;
|
|
22
|
+
subscribe(query: any, callback: RealtimeCallback): string;
|
|
23
|
+
unsubscribe(id: string): void;
|
|
24
|
+
private send;
|
|
25
|
+
private handleMessage;
|
|
26
|
+
private resubscribeAll;
|
|
27
|
+
private scheduleReconnect;
|
|
28
|
+
}
|
package/dist/realtime.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RealtimeClient = void 0;
|
|
4
|
+
class RealtimeClient {
|
|
5
|
+
constructor(config, auth) {
|
|
6
|
+
this.ws = null;
|
|
7
|
+
this.subscriptions = new Map();
|
|
8
|
+
this.reconnectTimer = null;
|
|
9
|
+
this.isConnected = false;
|
|
10
|
+
this.config = config;
|
|
11
|
+
this.auth = auth;
|
|
12
|
+
}
|
|
13
|
+
getUrl() {
|
|
14
|
+
const baseUrl = this.config.baseUrl || 'http://localhost:3000';
|
|
15
|
+
return baseUrl.replace(/^http/, 'ws') + '/v1/realtime';
|
|
16
|
+
}
|
|
17
|
+
connect() {
|
|
18
|
+
var _a;
|
|
19
|
+
if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING))
|
|
20
|
+
return;
|
|
21
|
+
const url = this.getUrl();
|
|
22
|
+
const headers = {
|
|
23
|
+
'x-api-key': this.config.apiKey
|
|
24
|
+
};
|
|
25
|
+
const token = (_a = this.auth.getSession()) === null || _a === void 0 ? void 0 : _a.access_token;
|
|
26
|
+
if (token) {
|
|
27
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
// Compatible with React Native (supports headers in 3rd arg) and standard browsers (ignores 3rd arg)
|
|
31
|
+
// @ts-ignore
|
|
32
|
+
this.ws = new WebSocket(url, [], { headers });
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
this.ws = new WebSocket(url);
|
|
36
|
+
}
|
|
37
|
+
if (!this.ws)
|
|
38
|
+
return;
|
|
39
|
+
this.ws.onopen = () => {
|
|
40
|
+
this.isConnected = true;
|
|
41
|
+
this.resubscribeAll();
|
|
42
|
+
};
|
|
43
|
+
this.ws.onmessage = (event) => {
|
|
44
|
+
try {
|
|
45
|
+
const msg = JSON.parse(event.data);
|
|
46
|
+
this.handleMessage(msg);
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
console.error('CoreBase Realtime parse error', e);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
this.ws.onclose = () => {
|
|
53
|
+
this.isConnected = false;
|
|
54
|
+
this.scheduleReconnect();
|
|
55
|
+
};
|
|
56
|
+
this.ws.onerror = (e) => {
|
|
57
|
+
console.error('CoreBase Realtime error', e);
|
|
58
|
+
// Do not close immediately on error, let onclose handle it?
|
|
59
|
+
// Sometimes error precedes close.
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
disconnect() {
|
|
63
|
+
if (this.ws) {
|
|
64
|
+
this.ws.close();
|
|
65
|
+
this.ws = null;
|
|
66
|
+
}
|
|
67
|
+
if (this.reconnectTimer) {
|
|
68
|
+
clearTimeout(this.reconnectTimer);
|
|
69
|
+
this.reconnectTimer = null;
|
|
70
|
+
}
|
|
71
|
+
this.isConnected = false;
|
|
72
|
+
}
|
|
73
|
+
subscribe(query, callback) {
|
|
74
|
+
const id = Math.random().toString(36).substring(7);
|
|
75
|
+
this.subscriptions.set(id, { query, callback });
|
|
76
|
+
if (this.isConnected) {
|
|
77
|
+
this.send({ type: 'subscribe', id, query });
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
this.connect();
|
|
81
|
+
}
|
|
82
|
+
return id;
|
|
83
|
+
}
|
|
84
|
+
unsubscribe(id) {
|
|
85
|
+
if (this.subscriptions.has(id)) {
|
|
86
|
+
this.subscriptions.delete(id);
|
|
87
|
+
if (this.isConnected) {
|
|
88
|
+
this.send({ type: 'unsubscribe', id });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
send(msg) {
|
|
93
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
94
|
+
this.ws.send(JSON.stringify(msg));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
handleMessage(msg) {
|
|
98
|
+
if (msg.type === 'data' && msg.id && msg.data) {
|
|
99
|
+
const sub = this.subscriptions.get(msg.id);
|
|
100
|
+
if (sub) {
|
|
101
|
+
sub.callback(msg.data);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else if (msg.type === 'error') {
|
|
105
|
+
console.error('CoreBase Realtime message error:', msg.error);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
resubscribeAll() {
|
|
109
|
+
this.subscriptions.forEach((sub, id) => {
|
|
110
|
+
this.send({ type: 'subscribe', id, query: sub.query });
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
scheduleReconnect() {
|
|
114
|
+
if (!this.reconnectTimer) {
|
|
115
|
+
this.reconnectTimer = setTimeout(() => {
|
|
116
|
+
this.reconnectTimer = null;
|
|
117
|
+
this.connect();
|
|
118
|
+
}, 3000);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
exports.RealtimeClient = RealtimeClient;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { AuthClient } from './auth';
|
|
2
|
+
import { ClientResponse } from './types';
|
|
3
|
+
export declare class StorageClient {
|
|
4
|
+
private auth;
|
|
5
|
+
constructor(auth: AuthClient);
|
|
6
|
+
/**
|
|
7
|
+
* Uploads a file to the storage bucket.
|
|
8
|
+
* This involves two steps:
|
|
9
|
+
* 1. Getting a signed upload URL from the API.
|
|
10
|
+
* 2. Uploading the file content directly to the cloud storage provider via the signed URL.
|
|
11
|
+
*
|
|
12
|
+
* @param file The File object to upload.
|
|
13
|
+
* @returns The key of the uploaded file.
|
|
14
|
+
*/
|
|
15
|
+
upload(file: File): Promise<ClientResponse<{
|
|
16
|
+
key: string;
|
|
17
|
+
url: string;
|
|
18
|
+
}>>;
|
|
19
|
+
}
|
package/dist/storage.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.StorageClient = void 0;
|
|
13
|
+
class StorageClient {
|
|
14
|
+
constructor(auth) {
|
|
15
|
+
this.auth = auth;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Uploads a file to the storage bucket.
|
|
19
|
+
* This involves two steps:
|
|
20
|
+
* 1. Getting a signed upload URL from the API.
|
|
21
|
+
* 2. Uploading the file content directly to the cloud storage provider via the signed URL.
|
|
22
|
+
*
|
|
23
|
+
* @param file The File object to upload.
|
|
24
|
+
* @returns The key of the uploaded file.
|
|
25
|
+
*/
|
|
26
|
+
upload(file) {
|
|
27
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
28
|
+
// 1. Get Signed URL
|
|
29
|
+
const { data: signData, error: signError } = yield this.auth.request('/storage/upload/sign', {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
body: JSON.stringify({
|
|
32
|
+
filename: file.name,
|
|
33
|
+
contentType: file.type,
|
|
34
|
+
size: file.size,
|
|
35
|
+
}),
|
|
36
|
+
});
|
|
37
|
+
if (signError) {
|
|
38
|
+
return { data: null, error: signError };
|
|
39
|
+
}
|
|
40
|
+
if (!signData) {
|
|
41
|
+
return { data: null, error: { message: 'Failed to retrieve signed upload URL.' } };
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
// 2. Upload File to Signed URL
|
|
45
|
+
const uploadResponse = yield fetch(signData.uploadUrl, {
|
|
46
|
+
method: 'PUT',
|
|
47
|
+
headers: {
|
|
48
|
+
'Content-Type': file.type,
|
|
49
|
+
},
|
|
50
|
+
body: file,
|
|
51
|
+
});
|
|
52
|
+
if (!uploadResponse.ok) {
|
|
53
|
+
return {
|
|
54
|
+
data: null,
|
|
55
|
+
error: {
|
|
56
|
+
message: `File upload failed with status ${uploadResponse.status}: ${uploadResponse.statusText}`
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// Return success with the key.
|
|
61
|
+
// The URL returned by sign is the upload URL (often with SAS/Signature params).
|
|
62
|
+
// Users typically store the 'key' to reference the file later.
|
|
63
|
+
return {
|
|
64
|
+
data: {
|
|
65
|
+
key: signData.key,
|
|
66
|
+
url: signData.uploadUrl // Returning the upload URL might be useful for immediate access if it's still valid/public-read
|
|
67
|
+
},
|
|
68
|
+
error: null
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
return { data: null, error: { message: err.message || 'Network error during file upload.' } };
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.StorageClient = StorageClient;
|
package/dist/types.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export interface ApiResponse<T> {
|
|
|
15
15
|
data?: T;
|
|
16
16
|
error?: {
|
|
17
17
|
message: string;
|
|
18
|
-
code
|
|
18
|
+
code?: string;
|
|
19
19
|
details?: string;
|
|
20
20
|
};
|
|
21
21
|
}
|
|
@@ -23,3 +23,12 @@ export interface ClientConfig {
|
|
|
23
23
|
apiKey: string;
|
|
24
24
|
baseUrl?: string;
|
|
25
25
|
}
|
|
26
|
+
export interface ClientResponse<T> {
|
|
27
|
+
data: T | null;
|
|
28
|
+
error: {
|
|
29
|
+
message: string;
|
|
30
|
+
code?: string;
|
|
31
|
+
details?: string;
|
|
32
|
+
} | null;
|
|
33
|
+
count?: number | null;
|
|
34
|
+
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "corebase-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "JavaScript client for CoreBase",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/index.js",
|
|
9
|
+
"./react": "./dist/react.js"
|
|
10
|
+
},
|
|
7
11
|
"scripts": {
|
|
8
12
|
"build": "tsc",
|
|
9
13
|
"prepublishOnly": "npm run build"
|
|
@@ -20,6 +24,16 @@
|
|
|
20
24
|
"license": "ISC",
|
|
21
25
|
"type": "commonjs",
|
|
22
26
|
"devDependencies": {
|
|
27
|
+
"@types/react": "^19.2.10",
|
|
28
|
+
"react": "^19.2.4",
|
|
23
29
|
"typescript": "^5.0.0"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"react": ">=16.8.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependenciesMeta": {
|
|
35
|
+
"react": {
|
|
36
|
+
"optional": true
|
|
37
|
+
}
|
|
24
38
|
}
|
|
25
39
|
}
|