solobase-js
Version:
A 100% drop-in replacement for the Supabase JavaScript client. Self-hosted Supabase alternative with complete API compatibility.
194 lines • 6.32 kB
JavaScript
import { SolobaseFetch } from './lib/fetch.js';
import { SolobaseAuthClient } from './SolobaseAuthClient.js';
import { SolobaseQueryBuilder } from './SolobaseQueryBuilder.js';
import { SolobaseStorageClient } from './SolobaseStorageClient.js';
import { SolobaseRealtimeClient } from './SolobaseRealtimeClient.js';
import { SolobaseSubscriptionClient } from './SolobaseSubscriptionClient.js';
import { CookieManager } from './lib/CookieManager.js';
/**
* Main Solobase client - 100% compatible with Supabase client API
*/
export class SolobaseClient {
constructor(baseUrl, apikey, options = {}) {
/**
* Create a custom function (for compatibility)
*/
this.functions = {
invoke: async (functionName, options) => {
const response = await this.fetch.post(`/functions/v1/${functionName}`, {
body: options?.body,
headers: options?.headers,
});
return {
data: response.data,
error: response.error ? {
message: response.error.message,
context: {},
} : null,
};
}
};
this.baseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
this.apikey = apikey;
this.options = options;
// Initialize cookie manager if cookies are provided (SSR mode)
if (options.cookies) {
const initialCookies = {};
if (options.cookies.getAll) {
const cookies = options.cookies.getAll();
cookies.forEach(({ name, value }) => {
initialCookies[name] = value;
});
}
this.cookieManager = new CookieManager(initialCookies, options.cookies);
}
// Initialize fetch client
this.fetch = new SolobaseFetch(this.baseUrl, this.apikey, options.global?.headers);
// Initialize auth client with cookie manager
this.auth = new SolobaseAuthClient(this.fetch, options.auth, this.cookieManager);
// Initialize storage client
this.storage = new SolobaseStorageClient(this.fetch);
// Initialize subscription client
this.subscription = new SolobaseSubscriptionClient(this.fetch);
// Initialize realtime client
this.realtime = new SolobaseRealtimeClient(this.baseUrl, options.realtime);
// Set up auth state change listener to update tokens
this.auth.onAuthStateChange((event, session) => {
const token = session?.access_token || null;
this.fetch.setAuth(token);
this.realtime.setAuth(token);
});
}
/**
* Create a query builder for a table
*
* @example
* ```js
* const { data, error } = await solobase
* .from('users')
* .select('*')
* .eq('id', 1)
* .single()
* ```
*/
from(table) {
return new SolobaseQueryBuilder(this.fetch, table, this.options.db?.schema || 'public');
}
/**
* Execute a raw SQL query with parameters
*
* @example
* ```js
* const { data, error } = await solobase.rpc('get_user_profile', { user_id: 1 })
* ```
*/
async rpc(fn, args = {}, options = {}) {
const headers = {};
if (options.head) {
headers['Prefer'] = 'head';
}
if (options.count) {
headers['Prefer'] = (headers['Prefer'] || '') + (headers['Prefer'] ? ',' : '') + `count=${options.count}`;
}
const response = await this.fetch.post(`/rest/v1/rpc/${fn}`, {
body: args,
headers,
});
return {
data: response.data,
error: response.error ? {
message: response.error.message,
details: '',
hint: '',
code: '',
} : null,
count: Array.isArray(response.data) ? response.data.length : (response.data ? 1 : 0),
status: response.status,
statusText: response.statusText,
};
}
/**
* Create a channel for real-time communication
*
* @example
* ```js
* const channel = solobase.channel('realtime:public:users')
* channel
* .on('postgres_changes', { event: '*', schema: 'public', table: 'users' }, (payload) => {
* console.log('Change received!', payload)
* })
* .subscribe()
* ```
*/
channel(topic, chanParams) {
return this.realtime.channel(topic, chanParams);
}
/**
* Get all active channels
*/
getChannels() {
return this.realtime.getChannels();
}
/**
* Remove a channel
*/
removeChannel(channel) {
return this.realtime.removeChannel(channel);
}
/**
* Remove all channels
*/
removeAllChannels() {
return this.realtime.removeAllChannels();
}
/**
* Listen for postgres changes on a table, schema, or database
*
* @example
* ```js
* solobase
* .channel('db-changes')
* .on('postgres_changes', {
* event: '*',
* schema: 'public',
* table: 'users'
* }, (payload) => console.log(payload))
* .subscribe()
* ```
*/
on(event, filter, callback) {
const topic = `realtime:${filter.schema}${filter.table ? `:${filter.table}` : ''}`;
const channel = this.channel(topic);
return channel.on(event, (payload) => {
// Filter based on the event type and other criteria
if (filter.event === '*' || payload.eventType === filter.event) {
callback(payload);
}
});
}
/**
* Close all connections and clean up
*/
removeAllSubscriptions() {
return this.removeAllChannels();
}
/**
* Get the current base URL
*/
get supabaseUrl() {
return this.baseUrl;
}
/**
* Get the current API key
*/
get supabaseKey() {
return this.apikey;
}
/**
* Access the raw REST endpoint (for advanced use cases)
*/
get rest() {
return this.fetch;
}
}
//# sourceMappingURL=SolobaseClient.js.map