pulse-ai-utils
Version:
Utility functions and helpers for AI-powered applications
121 lines • 6.37 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadServiceAccount = loadServiceAccount;
function loadServiceAccount() {
const fs = require('fs');
const path = require('path');
// 1) Via environment variable (raw JSON string)
if (process.env.FIREBASE_SERVICE_ACCOUNT_JSON) {
try {
// The environment variable may contain escaped JSON (e.g., from shell export)
let jsonString = process.env.FIREBASE_SERVICE_ACCOUNT_JSON;
// Remove any BOM (Byte Order Mark) characters that might be present
jsonString = jsonString.replace(/^\uFEFF/, '');
// Trim any leading/trailing whitespace
jsonString = jsonString.trim();
// If it starts and ends with quotes, remove them
if (jsonString.startsWith('"') && jsonString.endsWith('"')) {
jsonString = jsonString.slice(1, -1);
}
// Try parsing the JSON directly first (handles pretty-printed JSON)
try {
return JSON.parse(jsonString);
}
catch (directParseError) {
console.log('Direct JSON parse failed, trying cleanup:', directParseError.message);
}
// Handle control characters in the JSON string
// First, escape any literal newlines and other control characters to make valid JSON
jsonString = jsonString
.replace(/\n/g, '\\n') // escape literal newlines
.replace(/\r/g, '\\r') // escape literal carriage returns
.replace(/\t/g, '\\t') // escape literal tabs
.replace(/\b/g, '\\b') // escape literal backspace
.replace(/\f/g, '\\f') // escape literal form feed
.replace(/\v/g, '\\v'); // escape literal vertical tab
// Then handle escaped sequences if they exist
if (jsonString.includes('\\\\')) {
jsonString = jsonString
.replace(/\\\\n/g, '\\n') // fix double-escaped newlines
.replace(/\\\\r/g, '\\r') // fix double-escaped carriage returns
.replace(/\\\\t/g, '\\t') // fix double-escaped tabs
.replace(/\\\\"/g, '\\"') // fix double-escaped quotes
.replace(/\\\\'/g, "\\'") // fix double-escaped single quotes
.replace(/\\\\b/g, '\\b') // fix double-escaped backspace
.replace(/\\\\f/g, '\\f') // fix double-escaped form feed
.replace(/\\\\v/g, '\\v') // fix double-escaped vertical tab
.replace(/\\\\\//g, '\\/') // fix double-escaped forward slash
.replace(/\\\\\\/g, '\\\\'); // fix triple-escaped backslash
}
// Try to parse the JSON
return JSON.parse(jsonString);
}
catch (error) {
console.error('Failed to parse FIREBASE_SERVICE_ACCOUNT_JSON:', error.message);
console.error('JSON string length:', process.env.FIREBASE_SERVICE_ACCOUNT_JSON?.length);
console.error('First 200 chars:', process.env.FIREBASE_SERVICE_ACCOUNT_JSON?.substring(0, 200));
// Debug: Check for invisible characters at the beginning
const jsonStr = process.env.FIREBASE_SERVICE_ACCOUNT_JSON;
if (jsonStr) {
console.error('First 10 char codes:', jsonStr.substring(0, 10).split('').map(c => c.charCodeAt(0)));
}
// Try a different approach - Base64 decode if it looks like base64
if (jsonStr && !jsonStr.startsWith('{') && /^[A-Za-z0-9+/]+=*$/.test(jsonStr)) {
try {
console.log('Attempting base64 decode...');
const decoded = Buffer.from(jsonStr, 'base64').toString('utf8');
return JSON.parse(decoded);
}
catch (b64Error) {
console.error('Base64 decode also failed:', b64Error.message);
}
}
// Try removing all non-printable characters except newlines and tabs that should be escaped
try {
console.log('Attempting to clean non-printable characters...');
let cleanJson = jsonStr.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '');
cleanJson = cleanJson
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/\t/g, '\\t');
return JSON.parse(cleanJson);
}
catch (cleanError) {
console.error('Cleaned JSON parse also failed:', cleanError.message);
}
throw new Error(`Invalid FIREBASE_SERVICE_ACCOUNT_JSON format: ${error.message}`);
}
}
// 2) Via path from env variable
if (process.env.FIREBASE_SERVICE_ACCOUNT_PATH) {
try {
return JSON.parse(fs.readFileSync(process.env.FIREBASE_SERVICE_ACCOUNT_PATH, 'utf8'));
}
catch (err) {
/* ignore and continue to fallbacks */
}
}
// 3) <project-root>/configs/firebase-service-account.json (works from any CWD)
const rootPath = path.resolve(process.cwd(), 'configs', 'firebase-service-account.json');
try {
return JSON.parse(fs.readFileSync(rootPath, 'utf8'));
}
catch (_) {
/* ignore */
}
// 4) ../../configs/firebase-service-account.json relative to compiled dir (dist or src)
const fallbackPath = path.resolve(__dirname, '..', '..', 'configs', 'firebase-service-account.json');
try {
return JSON.parse(fs.readFileSync(fallbackPath, 'utf8'));
}
catch (_) {
/* ignore */
}
// In test mode, we skip Firebase initialization entirely, so this shouldn't be called
if (process.env.NODE_ENV === 'test') {
console.warn('loadServiceAccount called in test mode - this should not happen with proper lazy loading');
throw new Error('Firebase service account should not be loaded in test mode');
}
throw new Error('Unable to locate firebase-service-account.json. Provide it via FIREBASE_SERVICE_ACCOUNT_JSON, FIREBASE_SERVICE_ACCOUNT_PATH or place the file under <project-root>/configs.');
}
//# sourceMappingURL=load-service-account.js.map