appsmith-pocketbase
Version:
PocketBase integration library optimized for Appsmith with universal compatibility
754 lines (607 loc) • 23 kB
JavaScript
/**
* Appsmith PocketBase Library - UMD Version
* Compatible con Appsmith, CDN, y npm
*
* Versión: 1.1.0-umd
* GitHub: https://github.com/ingjorivera/as-pb-library-1.0
* CDN: https://cdn.jsdelivr.net/gh/ingjorivera/as-pb-library-1.0@main/appsmith-pb.js
*/
(function (root, factory) {
// UMD pattern para máxima compatibilidad
if (typeof define === 'function' && define.amd) {
// AMD (RequireJS)
define([], factory);
} else if (typeof module === 'object' && module.exports) {
// Node.js/CommonJS
module.exports = factory();
} else {
// Browser globals (incluye Appsmith)
root.AppsmithPB = factory();
}
}(typeof self !== 'undefined' ? self : this, function () {
'use strict';
const VERSION = '1.1.0-umd';
// ================================================================
// CONFIGURACIONES BASE
// ================================================================
const environmentConfigs = {
development: {
APP_VERSION: VERSION,
THEME_COLOR: '#ff6b6b',
ENABLE_DEBUG: true,
ENABLE_ANALYTICS: false,
ENABLE_ERROR_REPORTING: false,
SHOW_PERFORMANCE_METRICS: true,
DEFAULT_PAGE_SIZE: 10,
MAX_PAGE_SIZE: 100,
CACHE_TTL_MINUTES: 5,
JWT_EXPIRY_HOURS: 24,
REFRESH_TOKEN_EXPIRY_DAYS: 7,
SESSION_TIMEOUT_MINUTES: 60,
MAX_FILE_SIZE_MB: 10,
ALLOWED_FILE_TYPES: ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx'],
DB_TIMEOUT_MS: 5000,
ENABLE_DB_LOGGING: true,
AUTO_REFRESH_SESSION: true,
},
staging: {
APP_VERSION: VERSION,
THEME_COLOR: '#feca57',
ENABLE_DEBUG: true,
ENABLE_ANALYTICS: true,
ENABLE_ERROR_REPORTING: true,
SHOW_PERFORMANCE_METRICS: true,
DEFAULT_PAGE_SIZE: 20,
MAX_PAGE_SIZE: 200,
CACHE_TTL_MINUTES: 15,
JWT_EXPIRY_HOURS: 12,
REFRESH_TOKEN_EXPIRY_DAYS: 3,
SESSION_TIMEOUT_MINUTES: 30,
MAX_FILE_SIZE_MB: 50,
ALLOWED_FILE_TYPES: ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xls', 'xlsx'],
DB_TIMEOUT_MS: 8000,
ENABLE_DB_LOGGING: true,
AUTO_REFRESH_SESSION: true,
},
production: {
APP_VERSION: VERSION,
THEME_COLOR: '#5f27cd',
ENABLE_DEBUG: false,
ENABLE_ANALYTICS: true,
ENABLE_ERROR_REPORTING: true,
SHOW_PERFORMANCE_METRICS: false,
DEFAULT_PAGE_SIZE: 50,
MAX_PAGE_SIZE: 500,
CACHE_TTL_MINUTES: 60,
JWT_EXPIRY_HOURS: 2,
REFRESH_TOKEN_EXPIRY_DAYS: 30,
SESSION_TIMEOUT_MINUTES: 15,
MAX_FILE_SIZE_MB: 100,
ALLOWED_FILE_TYPES: ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'zip'],
DB_TIMEOUT_MS: 10000,
ENABLE_DB_LOGGING: false,
AUTO_REFRESH_SESSION: true,
}
};
// ================================================================
// STORAGE COMPATIBLE CON APPSMITH
// ================================================================
function createCompatibleStorage() {
// Detectar el entorno
const isAppsmith = typeof appsmith !== 'undefined';
const hasLocalStorage = typeof localStorage !== 'undefined';
return {
setItem: function(key, value) {
try {
// Prioridad 1: Appsmith storeValue
if (isAppsmith && typeof storeValue === 'function') {
storeValue(key, value, true);
return true;
}
// Prioridad 2: localStorage (si está disponible)
if (hasLocalStorage) {
localStorage.setItem(key, typeof value === 'string' ? value : JSON.stringify(value));
return true;
}
// Fallback: memoria (solo para esta sesión)
this._memoryStorage = this._memoryStorage || {};
this._memoryStorage[key] = value;
return true;
} catch (error) {
console.warn('[AppsmithPB] Storage setItem failed:', error);
return false;
}
},
getItem: function(key) {
try {
// Prioridad 1: Appsmith store
if (isAppsmith && typeof appsmith !== 'undefined' && appsmith.store && appsmith.store[key] !== undefined) {
return appsmith.store[key];
}
// Prioridad 2: localStorage
if (hasLocalStorage && localStorage.getItem(key) !== null) {
return localStorage.getItem(key);
}
// Fallback: memoria
if (this._memoryStorage && this._memoryStorage[key] !== undefined) {
return this._memoryStorage[key];
}
return null;
} catch (error) {
console.warn('[AppsmithPB] Storage getItem failed:', error);
return null;
}
},
removeItem: function(key) {
try {
// Appsmith
if (isAppsmith && typeof storeValue === 'function') {
storeValue(key, null, true);
}
// localStorage
if (hasLocalStorage) {
localStorage.removeItem(key);
}
// memoria
if (this._memoryStorage) {
delete this._memoryStorage[key];
}
} catch (error) {
console.warn('[AppsmithPB] Storage removeItem failed:', error);
}
}
};
}
// ================================================================
// CLASE DE CONFIGURACIÓN UNIVERSAL
// ================================================================
class UniversalEnvironment {
constructor(config = {}) {
this.currentEnv = config.environment || 'development';
this.customConfig = config;
this.mergedConfig = { ...environmentConfigs[this.currentEnv], ...config };
this.version = VERSION;
this.storage = createCompatibleStorage();
this.logEnvironmentInfo();
}
configure(config) {
this.customConfig = { ...this.customConfig, ...config };
this.mergedConfig = { ...environmentConfigs[this.currentEnv], ...this.customConfig };
this.log('Configuración actualizada', Object.keys(config));
return true;
}
get(key, defaultValue = null) {
return this.mergedConfig.hasOwnProperty(key) ? this.mergedConfig[key] : defaultValue;
}
has(key) {
return this.mergedConfig.hasOwnProperty(key);
}
getEnvironment() {
return this.currentEnv;
}
setEnvironment(env) {
if (['development', 'staging', 'production'].includes(env)) {
this.currentEnv = env;
this.mergedConfig = { ...environmentConfigs[env], ...this.customConfig };
this.log(`Entorno cambiado a: ${env}`);
return true;
}
return false;
}
isDevelopment() {
return this.currentEnv === 'development';
}
isStaging() {
return this.currentEnv === 'staging';
}
isProduction() {
return this.currentEnv === 'production';
}
getAll() {
return { ...this.mergedConfig };
}
validateRequiredConfig(requiredKeys) {
const missing = requiredKeys.filter(key => !this.has(key));
if (missing.length > 0) {
const error = new Error(`Configuración faltante: ${missing.join(', ')}`);
this.log('Validación falló:', error.message);
throw error;
}
return true;
}
log(message, data = null) {
if (!this.get('ENABLE_DEBUG')) return;
console.log(`🌍 [AppsmithPB v${VERSION}] ${message}`, data || '');
}
logEnvironmentInfo() {
if (!this.get('ENABLE_DEBUG')) return;
const platform = typeof appsmith !== 'undefined' ? 'Appsmith' :
typeof window !== 'undefined' ? 'Browser' : 'Node.js';
console.log(`🚀 AppsmithPB v${VERSION} inicializado en ${platform}`);
console.log(`📍 Entorno: ${this.currentEnv.toUpperCase()}`);
console.log(`🎨 Theme Color: ${this.get('THEME_COLOR')}`);
}
getExampleConfig() {
return {
environment: 'development',
APP_NAME: 'Mi App Appsmith',
POCKETBASE_URL: 'https://tu-pocketbase.fly.dev',
API_BASE_URL: 'https://tu-api.herokuapp.com/api',
};
}
}
// ================================================================
// CLASE POCKETBASE MANAGER UNIVERSAL
// ================================================================
class UniversalPocketBase {
constructor(env) {
this.env = env;
this.pbClient = null;
this.isInitialized = false;
this.authInfo = null;
}
init() {
if (this.isInitialized) return this.pbClient;
if (typeof PocketBase === 'undefined') {
throw new Error('[AppsmithPB] PocketBase library no está disponible. Instálala primero.');
}
const pbUrl = this.env.get('POCKETBASE_URL');
if (!pbUrl) {
throw new Error('[AppsmithPB] POCKETBASE_URL no configurado. Usar configure() primero.');
}
this.pbClient = new PocketBase(pbUrl);
this.setupTimeout();
this.isInitialized = true;
this.log('PocketBase inicializado', { url: this.maskUrl(pbUrl) });
return this.pbClient;
}
maskUrl(url) {
if (!url) return 'No configurado';
try {
const urlObj = new URL(url);
return `${urlObj.protocol}//${urlObj.hostname}${urlObj.port ? ':' + urlObj.port : ''}`;
} catch {
return 'URL inválida';
}
}
setupTimeout() {
const timeout = this.env.get('DB_TIMEOUT_MS', 10000);
const originalSend = this.pbClient.send;
this.pbClient.send = function(path, config = {}) {
return Promise.race([
originalSend.call(this, path, config),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('[AppsmithPB] Timeout de conexión')), timeout)
)
]);
};
}
getClient() {
if (!this.isInitialized) {
this.init();
}
return this.pbClient;
}
// ================================================================
// AUTENTICACIÓN
// ================================================================
async login(email, password) {
const pb = this.getClient();
try {
this.log('Iniciando login', { email });
const authData = await pb.collection('users').authWithPassword(email, password);
const jwtExpiry = this.env.get('JWT_EXPIRY_HOURS', 2) * 60 * 60 * 1000;
const refreshExpiry = this.env.get('REFRESH_TOKEN_EXPIRY_DAYS', 7) * 24 * 60 * 60 * 1000;
this.authInfo = {
token: authData.token,
user: authData.record,
expiresAt: Date.now() + jwtExpiry,
refreshExpiresAt: Date.now() + refreshExpiry,
environment: this.env.getEnvironment(),
loginTime: Date.now()
};
// Guardar en storage compatible
this.env.storage.setItem('appsmith_pb_auth', JSON.stringify(this.authInfo));
this.env.storage.setItem('pb_token', authData.token);
this.env.storage.setItem('pb_user', JSON.stringify(authData.record));
this.env.storage.setItem('pb_auth_env', this.env.getEnvironment());
this.log('Login exitoso', { userId: authData.record.id });
return authData;
} catch (error) {
this.logError('Error en login', error);
throw error;
}
}
async logout() {
const pb = this.getClient();
try {
pb.authStore.clear();
// Limpiar storage
this.env.storage.removeItem('appsmith_pb_auth');
this.env.storage.removeItem('pb_token');
this.env.storage.removeItem('pb_user');
this.env.storage.removeItem('pb_auth_env');
this.authInfo = null;
this.log('Logout exitoso');
return true;
} catch (error) {
this.logError('Error en logout', error);
throw error;
}
}
async validateSession() {
const pb = this.getClient();
try {
const storedAuth = this.env.storage.getItem('appsmith_pb_auth');
if (!storedAuth) return false;
this.authInfo = JSON.parse(storedAuth);
if (this.authInfo.environment !== this.env.getEnvironment()) {
this.log('Entorno cambió, limpiando sesión');
await this.logout();
return false;
}
const now = Date.now();
if (now > this.authInfo.refreshExpiresAt) {
this.log('Refresh token expirado');
await this.logout();
return false;
}
if (now > this.authInfo.expiresAt) {
this.log('Token expirado, intentando refresh');
try {
pb.authStore.save(this.authInfo.token, this.authInfo.user);
await pb.collection('users').authRefresh();
const jwtExpiry = this.env.get('JWT_EXPIRY_HOURS', 2) * 60 * 60 * 1000;
this.authInfo.token = pb.authStore.token;
this.authInfo.user = pb.authStore.model;
this.authInfo.expiresAt = Date.now() + jwtExpiry;
this.env.storage.setItem('appsmith_pb_auth', JSON.stringify(this.authInfo));
this.env.storage.setItem('pb_token', this.authInfo.token);
this.env.storage.setItem('pb_user', JSON.stringify(this.authInfo.user));
this.log('Token refrescado exitosamente');
return true;
} catch (refreshError) {
this.logError('Error al refrescar token', refreshError);
await this.logout();
return false;
}
}
pb.authStore.save(this.authInfo.token, this.authInfo.user);
this.log('Sesión válida');
return true;
} catch (error) {
this.logError('Error validando sesión', error);
await this.logout();
return false;
}
}
getCurrentUser() {
return this.authInfo?.user || null;
}
isAuthenticated() {
return !!(this.authInfo && Date.now() < this.authInfo.refreshExpiresAt);
}
// ================================================================
// CRUD OPERATIONS
// ================================================================
async create(collection, data) {
const pb = this.getClient();
try {
this.log(`Creando registro en ${collection}`, data);
const record = await pb.collection(collection).create(data);
this.log(`Registro creado en ${collection}`, { id: record.id });
return record;
} catch (error) {
this.logError(`Error creando en ${collection}`, error);
throw error;
}
}
async read(collection, page = 1, perPage = null, filter = '', sort = '') {
const pb = this.getClient();
if (!perPage) {
perPage = this.env.get('DEFAULT_PAGE_SIZE', 20);
}
const maxPageSize = this.env.get('MAX_PAGE_SIZE', 500);
if (perPage > maxPageSize) {
perPage = maxPageSize;
}
try {
this.log(`Leyendo ${collection}`, { page, perPage, filter, sort });
const records = await pb.collection(collection).getList(page, perPage, {
filter: filter,
sort: sort || '-created'
});
if (this.env.get('SHOW_PERFORMANCE_METRICS')) {
console.log(`📊 [PocketBase] ${records.items.length} registros de ${collection} (página ${records.page}/${records.totalPages})`);
}
return records;
} catch (error) {
this.logError(`Error leyendo ${collection}`, error);
throw error;
}
}
async getById(collection, id, expand = '') {
const pb = this.getClient();
try {
this.log(`Obteniendo ${collection}/${id}`);
const record = await pb.collection(collection).getOne(id, { expand: expand });
this.log(`Registro obtenido: ${collection}/${id}`);
return record;
} catch (error) {
this.logError(`Error obteniendo ${collection}/${id}`, error);
throw error;
}
}
async update(collection, id, data) {
const pb = this.getClient();
try {
this.log(`Actualizando ${collection}/${id}`, data);
const record = await pb.collection(collection).update(id, data);
this.log(`Registro actualizado: ${collection}/${id}`);
return record;
} catch (error) {
this.logError(`Error actualizando ${collection}/${id}`, error);
throw error;
}
}
async delete(collection, id) {
const pb = this.getClient();
try {
this.log(`Eliminando ${collection}/${id}`);
await pb.collection(collection).delete(id);
this.log(`Registro eliminado: ${collection}/${id}`);
return true;
} catch (error) {
this.logError(`Error eliminando ${collection}/${id}`, error);
throw error;
}
}
// ================================================================
// UTILIDADES
// ================================================================
async checkConnection() {
try {
const pb = this.getClient();
await pb.health.check();
return true;
} catch (error) {
this.logError('Error de conexión a PocketBase', error);
return false;
}
}
getSystemInfo() {
return {
version: VERSION,
environment: this.env.getEnvironment(),
pocketbaseUrl: this.maskUrl(this.env.get('POCKETBASE_URL')),
isAuthenticated: this.isAuthenticated(),
currentUser: this.getCurrentUser(),
platform: typeof appsmith !== 'undefined' ? 'Appsmith' :
typeof window !== 'undefined' ? 'Browser' : 'Node.js',
config: {
debugEnabled: this.env.get('ENABLE_DEBUG'),
analyticsEnabled: this.env.get('ENABLE_ANALYTICS')
}
};
}
log(message, data = null) {
if (!this.env.get('ENABLE_DB_LOGGING') && !this.env.get('ENABLE_DEBUG')) return;
console.log(`🗄️ [PocketBase] ${message}`, data || '');
}
logError(message, error) {
if (!this.env.get('ENABLE_DB_LOGGING') && !this.env.get('ENABLE_DEBUG')) return;
console.error(`❌ [PocketBase] ${message}`, error);
}
}
// ================================================================
// INICIALIZACIÓN
// ================================================================
const Environment = new UniversalEnvironment();
const PocketBaseManager = new UniversalPocketBase(Environment);
// ================================================================
// API PÚBLICA
// ================================================================
const AppsmithPB = {
version: VERSION,
Environment,
PocketBaseManager,
// Configuración
configure: function(config) {
const success = Environment.configure(config);
if (success) {
console.log('✅ [AppsmithPB] Configuración actualizada exitosamente');
try {
Environment.validateRequiredConfig(['POCKETBASE_URL']);
console.log('✅ [AppsmithPB] Configuración mínima completa');
} catch (error) {
console.warn('⚠️ [AppsmithPB]', error.message);
console.log('💡 Ejemplo:', Environment.getExampleConfig());
}
}
return success;
},
getConfigExample: function() {
return Environment.getExampleConfig();
},
checkConfig: function() {
const config = Environment.getAll();
console.group('🔧 Estado de Configuración AppsmithPB');
console.log('Entorno actual:', Environment.getEnvironment());
console.log('Variables configuradas:', Object.keys(config).length);
const requiredKeys = ['POCKETBASE_URL'];
const missingKeys = requiredKeys.filter(key => !Environment.has(key));
if (missingKeys.length > 0) {
console.warn('⚠️ Variables faltantes:', missingKeys);
console.log('💡 Configurar con: AppsmithPB.configure({ POCKETBASE_URL: "..." })');
} else {
console.log('✅ Configuración mínima completa');
}
console.groupEnd();
return missingKeys.length === 0;
},
setEnvironment: function(environment) {
return Environment.setEnvironment(environment);
},
// Funciones de entorno
env: function(key, defaultValue) {
return Environment.get(key, defaultValue);
},
isDev: function() {
return Environment.isDevelopment();
},
isStaging: function() {
return Environment.isStaging();
},
isProd: function() {
return Environment.isProduction();
},
getEnvironment: function() {
return Environment.getEnvironment();
},
// Autenticación
login: async function(email, password) {
return await PocketBaseManager.login(email, password);
},
logout: async function() {
return await PocketBaseManager.logout();
},
validateSession: async function() {
return await PocketBaseManager.validateSession();
},
getCurrentUser: function() {
return PocketBaseManager.getCurrentUser();
},
isAuthenticated: function() {
return PocketBaseManager.isAuthenticated();
},
// CRUD
create: async function(collection, data) {
return await PocketBaseManager.create(collection, data);
},
read: async function(collection, page, perPage, filter, sort) {
return await PocketBaseManager.read(collection, page, perPage, filter, sort);
},
getById: async function(collection, id, expand) {
return await PocketBaseManager.getById(collection, id, expand);
},
update: async function(collection, id, data) {
return await PocketBaseManager.update(collection, id, data);
},
delete: async function(collection, id) {
return await PocketBaseManager.delete(collection, id);
},
// Utilidades
checkConnection: async function() {
return await PocketBaseManager.checkConnection();
},
getSystemInfo: function() {
return PocketBaseManager.getSystemInfo();
},
getPocketBaseClient: function() {
return PocketBaseManager.getClient();
}
};
// Log de inicialización
console.log(`🚀 AppsmithPB v${VERSION} (UMD) cargado exitosamente`);
return AppsmithPB;
}));