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 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
- ```javascript
18
+ ```typescript
16
19
  import { createClient } from 'corebase-js';
17
20
 
18
- const corebase = createClient('https://your-project-url.com', 'your-public-api-key');
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
- ```javascript
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: 'password123',
38
+ password: 'securePassword123',
29
39
  name: 'John Doe'
30
40
  });
31
41
 
32
- if (error) {
33
- console.error('Signup failed:', error.message);
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
- ```javascript
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: 'password123'
53
+ password: 'securePassword123'
45
54
  });
46
55
 
47
- if (error) {
48
- console.error('Login failed:', error.message);
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
- ```javascript
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
- ```javascript
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
- private request;
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<AuthResponse<{
14
+ }): Promise<ClientResponse<{
23
15
  user: User;
24
16
  }>>;
25
17
  signIn(credentials: {
26
18
  email: string;
27
19
  password: string;
28
- }): Promise<AuthResponse<AuthSession>>;
29
- getUser(): Promise<AuthResponse<{
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
@@ -1,3 +1,6 @@
1
1
  export * from './types';
2
2
  export * from './client';
3
3
  export * from './auth';
4
+ export * from './query';
5
+ export * from './storage';
6
+ export * from './realtime';
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);
@@ -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;
@@ -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
+ }
@@ -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
+ }
@@ -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: string;
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.1.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
  }