livesheets-sdk
Version:
LiveSheets SDK for Google Sheets integration with Lovable apps. OAuth onboarding URL: https://livesheets-backend-624058308714.europe-north1.run.app/onboarding
235 lines (234 loc) • 8.53 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.livesheets = exports.auth = exports.LiveSheetsClient = exports.LiveSheetsAuth = void 0;
exports.readSheet = readSheet;
/**
* LiveSheetsAuth - Helper class for OAuth authentication
*/
class LiveSheetsAuth {
constructor(config = {}) {
this.backendUrl = config.backendUrl || LiveSheetsAuth.DEFAULT_BACKEND_URL;
this.jwtKey = config.storageKey || LiveSheetsAuth.DEFAULT_JWT_KEY;
this.userIdKey = config.userIdKey || LiveSheetsAuth.DEFAULT_USER_KEY;
}
/**
* Start OAuth flow
* @param returnUrl - Optional return URL after OAuth completion
*/
startOAuth(returnUrl) {
// Generate or retrieve user ID
const userId = this.getUserId();
// Use current URL as return URL if not provided
const currentUrl = returnUrl || (typeof window !== 'undefined' ? window.location.href.split('#')[0] : '');
const encodedReturnUrl = encodeURIComponent(currentUrl);
// Redirect to OAuth endpoint
if (typeof window !== 'undefined') {
window.location.href = `${this.backendUrl}/google-sheets/auth?sub=${userId}&return_url=${encodedReturnUrl}`;
}
}
/**
* Get JWT from storage or URL hash
*/
getJWT() {
if (typeof window === 'undefined')
return null;
// Check localStorage first
const storedJWT = localStorage.getItem(this.jwtKey);
if (storedJWT)
return storedJWT;
// Check URL hash for JWT (after OAuth redirect)
const hash = window.location.hash;
const match = hash.match(/jwt=([^&]+)/);
if (match) {
const jwt = match[1];
localStorage.setItem(this.jwtKey, jwt);
// Clean up URL
window.history.replaceState({}, document.title, window.location.pathname);
return jwt;
}
return null;
}
/**
* Check if user is authenticated
*/
isAuthenticated() {
return !!this.getJWT();
}
/**
* Clear authentication data
*/
logout() {
if (typeof window === 'undefined')
return;
localStorage.removeItem(this.jwtKey);
localStorage.removeItem(this.userIdKey);
}
/**
* Get or generate user ID
*/
getUserId() {
if (typeof window === 'undefined')
return 'user_server';
let userId = localStorage.getItem(this.userIdKey);
if (!userId) {
userId = `user_${Date.now()}`;
localStorage.setItem(this.userIdKey, userId);
}
return userId;
}
}
exports.LiveSheetsAuth = LiveSheetsAuth;
LiveSheetsAuth.DEFAULT_BACKEND_URL = 'https://livesheets-backend-624058308714.europe-north1.run.app';
LiveSheetsAuth.DEFAULT_JWT_KEY = 'livesheets_jwt';
LiveSheetsAuth.DEFAULT_USER_KEY = 'livesheets_user_id';
/**
* LiveSheetsClient SDK
*
* Example usage:
*
* import { LiveSheetsClient } from "livesheets-sdk";
* const client = new LiveSheetsClient("<your_jwt>");
* const rows = await client.listRows();
* const schema = await client.getSchema();
* const metadata = await client.getMetadata();
*/
class LiveSheetsClient {
constructor(jwt, baseUrl = "https://vfxfbuwrzbknqpwqmbqp.supabase.co/functions/v1") {
// Try to get JWT from auth helper if not provided
if (!jwt && typeof window !== 'undefined') {
const authHelper = LiveSheetsClient.getAuth();
jwt = authHelper.getJWT();
}
if (!jwt) {
throw new Error('JWT is required. Use LiveSheetsAuth to authenticate first.');
}
this.jwt = jwt;
this.baseUrl = baseUrl;
}
/**
* Get or create auth helper instance
*/
static getAuth(config) {
if (!this.auth) {
this.auth = new LiveSheetsAuth(config);
}
return this.auth;
}
/**
* Open sheet picker in popup window
*/
static async openSheetPicker(config) {
if (typeof window === 'undefined') {
throw new Error('Sheet picker can only be used in browser environment');
}
const { jwt, onSuccess, onCancel, width = 800, height = 600 } = config;
const backendUrl = this.auth?.backendUrl || 'https://livesheets-backend-624058308714.europe-north1.run.app';
// Calculate popup position
const left = (window.innerWidth - width) / 2;
const top = (window.innerHeight - height) / 2;
const popup = window.open(`${backendUrl}/onboarding#jwt=${jwt}`, 'GoogleSheetsPicker', `width=${width},height=${height},left=${left},top=${top}`);
if (!popup) {
throw new Error('Failed to open sheet picker popup. Please allow popups for this site.');
}
// Listen for sheet selection message
const handleMessage = (event) => {
if (event.origin !== backendUrl)
return;
if (event.data.type === 'sheet-selected') {
window.removeEventListener('message', handleMessage);
onSuccess?.({
spreadsheetId: event.data.spreadsheetId,
sheetName: event.data.sheetName
});
}
};
window.addEventListener('message', handleMessage);
// Handle popup close
const checkClosed = setInterval(() => {
if (popup.closed) {
clearInterval(checkClosed);
window.removeEventListener('message', handleMessage);
onCancel?.();
}
}, 500);
}
async request(endpoint) {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
headers: {
Authorization: `Bearer ${this.jwt}`,
"Content-Type": "application/json",
},
});
if (!response.ok) {
let errorMessage = `HTTP error! status: ${response.status}`;
try {
const errorData = await response.json();
errorMessage = errorData.detail || errorData.message || errorMessage;
}
catch {
// Ignore JSON parse errors
}
throw new Error(errorMessage);
}
return response.json();
}
async listRows() {
const data = await this.request("/sheets-listRows");
return data.rows;
}
async getSchema() {
const data = await this.request("/sheets-schema");
return data.schema;
}
async getMetadata() {
const data = await this.request("/sheets-metadata");
// Handle both old and new metadata format
return {
spreadsheetId: data.spreadsheetId || data.spreadsheet_id || '',
sheetName: data.sheetName || data.sheet_name || '',
rowCount: data.rowCount || data.row_count || 0,
columnCount: data.columnCount || data.column_count || 0
};
}
}
exports.LiveSheetsClient = LiveSheetsClient;
exports.livesheets = LiveSheetsClient;
// Helper function for simpler usage - supports multiple call signatures
async function readSheet(jwtOrParams, sheetIdOrOptions) {
let jwt;
let sheetId;
let sheet;
// Handle different parameter combinations
if (typeof jwtOrParams === 'string') {
// Called as readSheet(jwt) or readSheet(jwt, sheetId)
jwt = jwtOrParams;
if (typeof sheetIdOrOptions === 'string') {
sheetId = sheetIdOrOptions;
}
else if (sheetIdOrOptions) {
sheetId = sheetIdOrOptions.sheetId;
sheet = sheetIdOrOptions.sheet;
}
}
else if (jwtOrParams) {
// Called as readSheet({ jwt, sheetId, sheet })
jwt = jwtOrParams.jwt;
sheetId = jwtOrParams.sheetId;
sheet = jwtOrParams.sheet;
}
// Try to get JWT from auth helper if not provided
if (!jwt && typeof window !== 'undefined') {
const auth = LiveSheetsClient.getAuth();
jwt = auth.getJWT() || undefined;
}
if (!jwt) {
throw new Error('JWT is required to read sheet data. Either pass it as parameter or authenticate first.');
}
const client = new LiveSheetsClient(jwt);
// Note: sheetId and sheet are captured for future use but not used yet
return client.listRows();
}
// Export auth helper for standalone use
exports.auth = LiveSheetsClient.getAuth();
// Default export
exports.default = LiveSheetsClient;