uityu.shared
v1.0.6
Published
Shared infrastructure toolkit for UITYU microservices (Cloud Functions, SSR, APIs).
Maintainers
Readme
uityu.shared
Paquete compartido para los microservicios de UITYU. Expone infraestructura lista para producción (Firebase Admin, utilidades comunes, logging, normalización de texto, búsqueda, HTTP, etc.) con una API predecible y orientada a DDD.
Instalación
npm install uityu.shared firebase-adminFirebase Admin
Importación DX-first
import { getOrCreateFirebaseAdmin } from 'uityu.shared/firebase/admin';Uso básico (SSR, Cloud Functions, APIs)
import { getOrCreateFirebaseAdmin } from 'uityu.shared/firebase/admin';
const firebase = getOrCreateFirebaseAdmin({
projectId: 'my-project',
defaultFirestoreAlias: 'primary',
defaultFirestoreDbId: '(default)'
});
await firebase.defaultFirestore.collection('users').doc('123').get();Multi Firestore con alias
import { getOrCreateFirebaseAdmin } from 'uityu.shared/firebase/admin';
const firebase = getOrCreateFirebaseAdmin({
projectId: 'analytics-project',
firestoreDatabases: [
{ alias: 'default', dbId: '(default)' },
{ alias: 'audit', dbId: 'audit-db' },
{ alias: 'analytics', dbId: 'analytics-db' }
]
});
const auditDb = firebase.getFirestore('audit');
const analyticsDb = firebase.getFirestore('analytics');
await auditDb.collection('events').add({ type: 'ACCESS', createdAt: Date.now() });
await analyticsDb.collection('dashboards').doc('overview').set({ views: 420 });Alias impulsados por variables de entorno
APP_PROJECT_ID=my-shared-project
FIRESTORE_DB_DEFAULT=(default)
FIRESTORE_DB_ALIASES={"audit":"audit-db","analytics":"analytics-db"}import { getOrCreateFirebaseAdmin } from 'uityu.shared/firebase/admin';
const firebase = getOrCreateFirebaseAdmin({
env: () => ({
APP_PROJECT_ID: process.env.APP_PROJECT_ID,
FIRESTORE_DB_DEFAULT: process.env.FIRESTORE_DB_DEFAULT,
FIRESTORE_DB_ALIASES: process.env.FIRESTORE_DB_ALIASES
})
});
const primaryDb = firebase.getFirestore();
const auditDb = firebase.getFirestore('audit');Uso avanzado con credenciales y Storage
import { createFirebaseAdmin } from 'uityu.shared/firebase/admin';
import * as admin from 'firebase-admin';
const firebase = createFirebaseAdmin({
projectId: 'secure-project',
credential: admin.credential.cert(JSON.parse(process.env.FIREBASE_SA_JSON ?? '{}')),
storageBucket: 'secure-project.appspot.com',
firestoreDbAliases: {
default: '(default)',
media: 'media-db'
},
appName: 'secure-project::primary'
});
await firebase.auth.getUser('uid');
await firebase.storage.bucket().file('path/to/file');Idempotente por diseño
createFirebaseAdmincrea una instancia nueva y falla si ya existe ese contexto.getOrCreateFirebaseAdminreutiliza la instancia si ya fue creada (ideal para SSR/Functions).
Facade expuesta
Cada factory devuelve un objeto con:
app: instancia defirebase-admin.defaultFirestore: instancia asociada al alias predeterminado.firestores: mapa alias -> instancia lazily inicializada.getFirestore(alias?): helper para resolver una base por alias (usa el alias predeterminado si se omite).auth:Authinicializado.storage:Storageinicializado.
Common utilities (@uityu/uityu-shared/common/*)
Logging listo para JSON
import { logInfo, logError } from 'uityu.shared/common/logging';
logInfo({
namespace: 'Orders.Create',
message: 'Order persisted',
metadata: { orderId: 'ord_123', attempt: 1 }
});
logError({
namespace: 'Orders.Create',
message: 'Failed to persist',
error: new Error('timeout')
});Utilidades de matemática
import { clampLimit } from 'uityu.shared/common/math';
const limit = clampLimit(undefined, { defaultValue: 10, max: 50, min: 5 });
// -> 10Normalización de texto genérica
import { normalizeText } from 'uityu.shared/common/string';
const emailKey = normalizeText(' [email protected] ');
// -> '[email protected]'
const keepCase = normalizeText(' Mixed Value ', { lowercase: false });
// -> 'Mixed Value'Errores + HTTP (@uityu/uityu-shared/errors, @uityu/uityu-shared/http)
import { AppErrorFactory } from 'uityu.shared/errors';
import { toApiError } from 'uityu.shared/http';
export async function handler(req): Promise<Response> {
try {
if (!req.headers.authorization) {
throw AppErrorFactory.authRequired();
}
// ...
} catch (error) {
const { status, body } = toApiError(error, req.headers['x-trace-id']);
return new Response(JSON.stringify(body), { status });
}
}Middleware CORS configurable
import { createCorsToolkit } from 'uityu.shared/http';
const cors = createCorsToolkit({
allowedOrigins: ['https://app.uityu.com', 'https://admin.uityu.com'],
allowedMethods: ['GET', 'POST'],
allowedHeaders: ['Content-Type', 'Authorization'],
exposeHeaders: ['X-Trace-Id'],
allowCredentials: true,
});
export const handler = (req, res, next) => {
cors.corsHandler(req, res, (err) => {
if (err) return next(err);
if (!cors.ensureMethod(req, res, 'POST')) return;
// ...logic
next();
});
};Búsqueda (@uityu/uityu-shared/search)
import {
buildSearchPrefixes,
createEmptySearchIndex,
normalizePhoneDigits,
normalizeSearchText
} from 'uityu.shared/search';
const normalized = normalizeSearchText('Crème Brûlée #42');
const phone = normalizePhoneDigits('+51 999 888 777');
const prefixes = buildSearchPrefixes([normalized, phone]);
const index = createEmptySearchIndex<string>();
for (const prefix of prefixes) {
index[prefix] = 'recipe-id';
}Por qué adoptar este paquete
- Sin copy/paste entre microservicios.
- API estable y versionable (semver).
- Facilita pruebas e inyección de dependencias.
- Alineado con DDD y SOLID.
- Preparado para extender con
firebase/functions,firebase/pubsub, etc.
Roadmap sugerido
- Añadir módulos especializados (
firebase/functions,firebase/pubsub). - Versionar con semver y publicar en npm privado o GitHub Packages.
- Crear tests de contrato para los microservicios que consuman este paquete.
