Function bodies 126 total
handleAdd function · javascript · L66-L97 (32 LOC)public/js/components/companySelector.js
function handleAdd() {
const ticker = input.value.trim().toUpperCase();
if (!ticker) return;
errorMsg.textContent = '';
// Already in portfolio
if (opts.portfolioTickers.includes(ticker)) {
errorMsg.textContent = 'Already in portfolio';
return;
}
// Already added as comparison
if (opts.comparisonCompanies.some(c => c.ticker === ticker)) {
errorMsg.textContent = 'Already added';
return;
}
// Check availability — if missing, log a request
if (!opts.availableTickers.includes(ticker)) {
errorMsg.textContent = `No data for ${ticker}`;
API.requestComparison(ticker).then(result => {
errorMsg.textContent = result.alreadyRequested
? `No data for ${ticker} \u2014 already requested`
: `No data for ${ticker} \u2014 requested`;
}).catch(() => {});
return;
}
input.value = '';
opts.onAddComparison(ticker);
}heatmapQuarterLabel function · javascript · L76-L79 (4 LOC)public/js/components/heatmap.js
function heatmapQuarterLabel(q, useCalendar) {
if (useCalendar) return (q.calendarQuarter || q.quarter).replace(' FY', ' ');
return q.quarter.replace(' FY', ' ');
}quarterSort function · javascript · L81-L86 (6 LOC)public/js/components/heatmap.js
function quarterSort(a, b) {
const pa = parseQuarterLabel(a);
const pb = parseQuarterLabel(b);
if (pa.year !== pb.year) return pa.year - pb.year;
return pa.q - pb.q;
}parseQuarterLabel function · javascript · L88-L92 (5 LOC)public/js/components/heatmap.js
function parseQuarterLabel(label) {
const m = label.match(/Q(\d)\s+(\d{4})/);
if (m) return { q: parseInt(m[1]), year: parseInt(m[2]) };
return { q: 0, year: 0 };
}doSort function · javascript · L23-L39 (17 LOC)public/js/components/sortableTable.js
function doSort() {
sortedData = [...data].sort((a, b) => {
let av = a[sortKey], bv = b[sortKey];
// Handle nulls
if (av === null || av === undefined || av === 'N/A') av = sortDir === 'asc' ? Infinity : -Infinity;
if (bv === null || bv === undefined || bv === 'N/A') bv = sortDir === 'asc' ? Infinity : -Infinity;
// Numeric comparison
if (typeof av === 'number' && typeof bv === 'number') {
return sortDir === 'asc' ? av - bv : bv - av;
}
// String comparison
return sortDir === 'asc'
? String(av).localeCompare(String(bv))
: String(bv).localeCompare(String(av));
});
renderTable();
}renderTable function · javascript · L41-L98 (58 LOC)public/js/components/sortableTable.js
function renderTable() {
const headerHtml = columns.map(col => {
const sortable = col.sortable !== false;
const arrow = sortKey === col.key ? (sortDir === 'asc' ? ' \u25B2' : ' \u25BC') : '';
const cls = [
'th',
col.align === 'right' ? 'text-right' : '',
sortable ? 'sortable' : '',
].filter(Boolean).join(' ');
return `<div class="${cls}" data-key="${col.key}">${col.label}${arrow}</div>`;
}).join('');
const rowsHtml = sortedData.map((row, i) => {
const isComp = row._isComparison;
const cells = columns.map(col => {
let val = row[col.key];
if (col.format) val = col.format(val, row);
else if (val === null || val === undefined) val = 'N/A';
const clsList = [col.align === 'right' ? 'text-right' : ''];
if (isComp && col.key === 'ticker') clsList.push('comparison-ticker');
const cls = clsList.filter(Boolean).join(' ');
applyFilters function · javascript · L40-L52 (13 LOC)public/js/dashboards/growth.js
function applyFilters() {
const accelOnly = document.getElementById('growth-filter-accel')?.checked || false;
const profitOnly = document.getElementById('growth-filter-profit')?.checked || false;
const useCalendarQuarters = document.getElementById('growth-filter-calendar')?.checked || false;
let filtered = companies.filter(c => {
if (accelOnly && c.calculated?.momentum?.trend !== 'accelerating') return false;
if (profitOnly && c.currentlyProfitable !== true) return false;
return true;
});
self._renderData(dataContainer, filtered, companies, useCalendarQuarters);
}Want this analysis on your repo? https://repobility.com/scan/
quarterLabel function · javascript · L183-L186 (4 LOC)public/js/dashboards/growth.js
function quarterLabel(q, useCalendar) {
if (useCalendar) return (q.calendarQuarter || q.quarter).replace(' FY', ' ');
return q.quarter.replace(' FY', ' ');
}quarterSort function · javascript · L188-L193 (6 LOC)public/js/dashboards/growth.js
function quarterSort(a, b) {
const pa = parseQuarterLabel(a);
const pb = parseQuarterLabel(b);
if (pa.year !== pb.year) return pa.year - pb.year;
return pa.q - pb.q;
}parseQuarterLabel function · javascript · L195-L199 (5 LOC)public/js/dashboards/growth.js
function parseQuarterLabel(label) {
const m = label.match(/Q(\d)\s+(\d{4})/);
if (m) return { q: parseInt(m[1]), year: parseInt(m[2]) };
return { q: 0, year: 0 };
}decodeBase64Url function · javascript · L9-L14 (6 LOC)server/auth/accessAuth.js
function decodeBase64Url(input) {
const normalized = input.replace(/-/g, '+').replace(/_/g, '/');
const padding = normalized.length % 4;
const padded = padding ? normalized + '='.repeat(4 - padding) : normalized;
return Buffer.from(padded, 'base64');
}parseJwt function · javascript · L16-L37 (22 LOC)server/auth/accessAuth.js
function parseJwt(token) {
const parts = String(token || '').split('.');
if (parts.length !== 3) {
throw new Error('Malformed JWT');
}
let header;
let payload;
try {
header = JSON.parse(decodeBase64Url(parts[0]).toString('utf8'));
payload = JSON.parse(decodeBase64Url(parts[1]).toString('utf8'));
} catch (err) {
throw new Error('Invalid JWT encoding');
}
return {
header,
payload,
signature: decodeBase64Url(parts[2]),
signingInput: `${parts[0]}.${parts[1]}`,
};
}JwksCache class · javascript · L39-L94 (56 LOC)server/auth/accessAuth.js
class JwksCache {
constructor(teamDomain) {
this.teamDomain = teamDomain;
this.keys = new Map();
this.expiresAt = 0;
this.pendingFetch = null;
}
async getKey(kid) {
if (!kid) {
throw new Error('JWT missing key id');
}
const now = Date.now();
if (this.keys.has(kid) && now < this.expiresAt) {
return this.keys.get(kid);
}
if (!this.pendingFetch) {
this.pendingFetch = this.refresh().finally(() => {
this.pendingFetch = null;
});
}
await this.pendingFetch;
const key = this.keys.get(kid);
if (!key) {
throw new Error(`Unknown JWT key id: ${kid}`);
}
return key;
}
async refresh() {
const url = `https://${this.teamDomain}/cdn-cgi/access/certs`;
const body = await fetchJson(url);
const keys = Array.isArray(body.keys) ? body.keys : [];
if (keys.length === 0) {
throw new Error('Cloudflare cert endpoint returned no keys');
}
const nextKeys = new Map();
constructor method · javascript · L40-L45 (6 LOC)server/auth/accessAuth.js
constructor(teamDomain) {
this.teamDomain = teamDomain;
this.keys = new Map();
this.expiresAt = 0;
this.pendingFetch = null;
}getKey method · javascript · L47-L69 (23 LOC)server/auth/accessAuth.js
async getKey(kid) {
if (!kid) {
throw new Error('JWT missing key id');
}
const now = Date.now();
if (this.keys.has(kid) && now < this.expiresAt) {
return this.keys.get(kid);
}
if (!this.pendingFetch) {
this.pendingFetch = this.refresh().finally(() => {
this.pendingFetch = null;
});
}
await this.pendingFetch;
const key = this.keys.get(kid);
if (!key) {
throw new Error(`Unknown JWT key id: ${kid}`);
}
return key;
}Repobility — same analyzer, your code, free for public repos · /scan/
refresh method · javascript · L71-L93 (23 LOC)server/auth/accessAuth.js
async refresh() {
const url = `https://${this.teamDomain}/cdn-cgi/access/certs`;
const body = await fetchJson(url);
const keys = Array.isArray(body.keys) ? body.keys : [];
if (keys.length === 0) {
throw new Error('Cloudflare cert endpoint returned no keys');
}
const nextKeys = new Map();
keys.forEach(jwk => {
if (jwk && jwk.kid && jwk.kty === 'RSA') {
const publicKey = crypto.createPublicKey({ key: jwk, format: 'jwk' });
nextKeys.set(jwk.kid, publicKey);
}
});
if (nextKeys.size === 0) {
throw new Error('Cloudflare cert endpoint returned no RSA keys');
}
this.keys = nextKeys;
this.expiresAt = Date.now() + JWKS_CACHE_TTL_MS;
}fetchJson function · javascript · L96-L128 (33 LOC)server/auth/accessAuth.js
function fetchJson(url) {
return new Promise((resolve, reject) => {
const req = https.get(
url,
{
headers: {
Accept: 'application/json',
},
},
res => {
let data = '';
res.setEncoding('utf8');
res.on('data', chunk => {
data += chunk;
});
res.on('end', () => {
if (res.statusCode < 200 || res.statusCode >= 300) {
reject(new Error(`Failed to fetch Cloudflare certs: ${res.statusCode}`));
return;
}
try {
resolve(JSON.parse(data));
} catch (err) {
reject(new Error('Cloudflare cert endpoint returned invalid JSON'));
}
});
}
);
req.on('error', reject);
});
}verifyAudience function · javascript · L130-L135 (6 LOC)server/auth/accessAuth.js
function verifyAudience(payloadAud, expectedAudience) {
if (Array.isArray(payloadAud)) {
return payloadAud.includes(expectedAudience);
}
return payloadAud === expectedAudience;
}assertClaimChecks function · javascript · L137-L153 (17 LOC)server/auth/accessAuth.js
function assertClaimChecks(payload, config) {
const now = Math.floor(Date.now() / 1000);
if (payload.exp && now >= payload.exp) {
throw new Error('JWT expired');
}
if (payload.nbf && now < payload.nbf) {
throw new Error('JWT not active yet');
}
if (!verifyAudience(payload.aud, config.audience)) {
throw new Error('JWT audience mismatch');
}
const expectedIssuer = `https://${config.teamDomain}`;
if (payload.iss !== expectedIssuer) {
throw new Error('JWT issuer mismatch');
}
}extractEmail function · javascript · L155-L162 (8 LOC)server/auth/accessAuth.js
function extractEmail(payload) {
const email = payload.email || payload.sub || '';
const normalized = String(email).trim().toLowerCase();
if (!normalized.includes('@')) {
throw new Error('JWT missing email claim');
}
return normalized;
}createAccessAuth function · javascript · L164-L239 (76 LOC)server/auth/accessAuth.js
function createAccessAuth(options = {}) {
const config = {
...getAccessConfig(options.env),
...(options.config || {}),
};
const problems = validateConfig(config);
if (problems.length > 0) {
throw new Error(`Access auth misconfigured: ${problems.join('; ')}`);
}
const jwks = options.jwksCache || new JwksCache(config.teamDomain);
async function authenticateRequest(req) {
const canUseDevOverride = config.nodeEnv !== 'production' && config.devAccessEmail;
if (canUseDevOverride) {
const role = getRoleForEmail(config.devAccessEmail, config);
if (!role) {
throw new Error('DEV_ACCESS_EMAIL is not on an approved access list');
}
return {
email: config.devAccessEmail,
role,
subject: config.devAccessEmail,
issuer: 'dev-override',
};
}
const token = req.headers[ACCESS_JWT_HEADER];
if (!token) {
throw new Error('Missing Cloudflare Access JWT');
}
const parsed = parauthenticateRequest function · javascript · L177-L225 (49 LOC)server/auth/accessAuth.js
async function authenticateRequest(req) {
const canUseDevOverride = config.nodeEnv !== 'production' && config.devAccessEmail;
if (canUseDevOverride) {
const role = getRoleForEmail(config.devAccessEmail, config);
if (!role) {
throw new Error('DEV_ACCESS_EMAIL is not on an approved access list');
}
return {
email: config.devAccessEmail,
role,
subject: config.devAccessEmail,
issuer: 'dev-override',
};
}
const token = req.headers[ACCESS_JWT_HEADER];
if (!token) {
throw new Error('Missing Cloudflare Access JWT');
}
const parsed = parseJwt(token);
const publicKey = await jwks.getKey(parsed.header.kid);
const isValid = crypto.verify(
'RSA-SHA256',
Buffer.from(parsed.signingInput),
publicKey,
parsed.signature
);
if (!isValid) {
throw new Error('Invalid Cloudflare Access JWT signature');
}
assertClaimChecks(parsed.payload, config);
accessAuth function · javascript · L227-L234 (8 LOC)server/auth/accessAuth.js
async function accessAuth(req, res, next) {
try {
req.user = await authenticateRequest(req);
next();
} catch (err) {
next(err);
}
}Methodology: Repobility · https://repobility.com/research/state-of-ai-code-2026/
isHtmlRequest function · javascript · L1-L3 (3 LOC)server/auth/authorize.js
function isHtmlRequest(req) {
return req.accepts(['html', 'json']) === 'html';
}renderAuthErrorPage function · javascript · L5-L65 (61 LOC)server/auth/authorize.js
function renderAuthErrorPage(statusCode, title, message) {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${title}</title>
<style>
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
background: #0a0e17;
color: #e2e8f0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
main {
width: min(560px, calc(100vw - 32px));
padding: 32px;
border: 1px solid rgba(255, 255, 255, 0.08);
background: #151d2e;
border-radius: 8px;
}
h1 {
margin: 0 0 12px;
color: #ff6600;
font-size: 20px;
letter-spacing: 0.04em;
text-transform: uppercase;
}
p {
margin: 0 0 16px;
color: #cbd5e1;
line-height: 1.6;
}
a {
color: #ff6600;
text-decoration: none;
}
a:hover {
text-decoration: undsendUnauthorized function · javascript · L67-L77 (11 LOC)server/auth/authorize.js
function sendUnauthorized(res, req, statusCode, message) {
if (isHtmlRequest(req)) {
const title = statusCode === 401 ? 'Authentication Required' : 'Access Restricted';
return res
.status(statusCode)
.type('html')
.send(renderAuthErrorPage(statusCode, title, message));
}
return res.status(statusCode).json({ error: message });
}requireAuth function · javascript · L79-L84 (6 LOC)server/auth/authorize.js
function requireAuth(req, res, next) {
if (req.user) {
return next();
}
return sendUnauthorized(res, req, 401, 'A valid Cloudflare Access session is required.');
}requireRole function · javascript · L86-L105 (20 LOC)server/auth/authorize.js
function requireRole(role) {
return (req, res, next) => {
if (!req.user) {
return sendUnauthorized(res, req, 401, 'A valid Cloudflare Access session is required.');
}
if (req.user.role === role) {
return next();
}
console.warn(
`[auth] forbidden role=${req.user.role} email=${req.user.email} path=${req.originalUrl}`
);
return sendUnauthorized(
res,
req,
403,
'Your account is signed in, but this area is restricted to the family tier.'
);
};
}authErrorHandler function · javascript · L107-L110 (4 LOC)server/auth/authorize.js
function authErrorHandler(err, req, res, next) { // eslint-disable-line no-unused-vars
console.warn(`[auth] rejected path=${req.originalUrl} reason=${err.message}`);
return sendUnauthorized(res, req, 401, 'Your Cloudflare Access session could not be verified.');
}parseCsvList function · javascript · L1-L8 (8 LOC)server/auth/config.js
function parseCsvList(value) {
return new Set(
String(value || '')
.split(',')
.map(item => item.trim().toLowerCase())
.filter(Boolean)
);
}normalizeDomain function · javascript · L10-L14 (5 LOC)server/auth/config.js
function normalizeDomain(value) {
const trimmed = String(value || '').trim().replace(/\/+$/, '');
if (!trimmed) return '';
return trimmed.replace(/^https?:\/\//i, '').toLowerCase();
}Want fix-PRs on findings? Install Repobility's GitHub App · github.com/apps/repobility-bot
getAccessConfig function · javascript · L16-L26 (11 LOC)server/auth/config.js
function getAccessConfig(env = process.env) {
return {
audience: String(env.CLOUDFLARE_ACCESS_AUD || '').trim(),
teamDomain: normalizeDomain(env.CLOUDFLARE_TEAM_DOMAIN),
familyEmails: parseCsvList(env.FAMILY_EMAILS),
generalEmails: parseCsvList(env.ALLOW_GENERAL_EMAILS),
generalDomains: parseCsvList(env.ALLOW_GENERAL_DOMAINS),
devAccessEmail: String(env.DEV_ACCESS_EMAIL || '').trim().toLowerCase(),
nodeEnv: String(env.NODE_ENV || '').trim().toLowerCase(),
};
}hasGeneralAccessConfig function · javascript · L28-L30 (3 LOC)server/auth/config.js
function hasGeneralAccessConfig(config) {
return config.generalEmails.size > 0 || config.generalDomains.size > 0;
}getRoleForEmail function · javascript · L32-L44 (13 LOC)server/auth/config.js
function getRoleForEmail(email, config) {
const normalizedEmail = String(email || '').trim().toLowerCase();
if (!normalizedEmail) return null;
if (config.familyEmails.has(normalizedEmail)) return 'family';
if (config.generalEmails.has(normalizedEmail)) return 'general';
const atIndex = normalizedEmail.lastIndexOf('@');
if (atIndex === -1) return null;
const domain = normalizedEmail.slice(atIndex + 1);
if (config.generalDomains.has(domain)) return 'general';
return null;
}validateConfig function · javascript · L46-L57 (12 LOC)server/auth/config.js
function validateConfig(config) {
const problems = [];
const isDevOverride = config.nodeEnv !== 'production' && !!config.devAccessEmail;
if (!config.audience && !isDevOverride) problems.push('CLOUDFLARE_ACCESS_AUD is required');
if (!config.teamDomain && !isDevOverride) problems.push('CLOUDFLARE_TEAM_DOMAIN is required');
if (config.familyEmails.size === 0) problems.push('FAMILY_EMAILS must contain at least one email');
if (!hasGeneralAccessConfig(config)) {
problems.push('ALLOW_GENERAL_EMAILS or ALLOW_GENERAL_DOMAINS must be configured');
}
return problems;
}sequentialMomentum function · javascript · L13-L48 (36 LOC)server/calculator.js
function sequentialMomentum(company) {
const hist = company.quarterlyHistory;
if (!hist || hist.length < 3) return null;
// Compute QoQ from revenue if not directly available
let currentQoq, priorQoq;
if (hist[0].revenueQoqPct !== null && hist[0].revenueQoqPct !== undefined) {
currentQoq = hist[0].revenueQoqPct;
} else if (hist[0].revenueMil && hist[1].revenueMil) {
currentQoq = ((hist[0].revenueMil - hist[1].revenueMil) / hist[1].revenueMil) * 100;
} else {
return null;
}
if (hist[1].revenueQoqPct !== null && hist[1].revenueQoqPct !== undefined) {
priorQoq = hist[1].revenueQoqPct;
} else if (hist[1].revenueMil && hist[2].revenueMil) {
priorQoq = ((hist[1].revenueMil - hist[2].revenueMil) / hist[2].revenueMil) * 100;
} else {
return null;
}
const delta = currentQoq - priorQoq;
let trend;
if (delta > 2) trend = 'accelerating';
else if (delta < -2) trend = 'decelerating';
else trend = 'stable';
return {
currentQoq: MathgrowthAdjustedValuation function · javascript · L56-L61 (6 LOC)server/calculator.js
function growthAdjustedValuation(company) {
const pe = company.runRatePe || company.trailingPe || company.normalizedPe;
const growth = company.revenueYoyPct;
if (!pe || !growth || growth <= 0) return null;
return Math.round((pe / growth) * 100) / 100;
}operatingLeverage function · javascript · L69-L87 (19 LOC)server/calculator.js
function operatingLeverage(company) {
const currentEBITDA = company.ebitdaMil;
const currentRevenue = company.revenueRecentMil;
const ebitdaYoY = company.ebitdaYoyPct;
const revenueYoY = company.revenueYoyPct;
if (currentEBITDA == null || currentRevenue == null ||
ebitdaYoY == null || revenueYoY == null ||
revenueYoY === 0 || ebitdaYoY === -100) return null;
// Back-calculate prior-year values from current + YoY growth
const priorEBITDA = currentEBITDA / (1 + ebitdaYoY / 100);
const priorRevenue = currentRevenue / (1 + revenueYoY / 100);
const revenueDelta = currentRevenue - priorRevenue;
if (revenueDelta === 0) return null;
const ebitdaDelta = currentEBITDA - priorEBITDA;
return Math.round((ebitdaDelta / revenueDelta) * 100) / 100;
}distanceFrom52WeekHigh function · javascript · L93-L98 (6 LOC)server/calculator.js
function distanceFrom52WeekHigh(company) {
const price = company.price;
const high = company.fiftyTwoWeekHigh;
if (!price || !high) return null;
return Math.round(((price - high) / high) * 10000) / 100;
}Want this analysis on your repo? https://repobility.com/scan/
peCompression function · javascript · L105-L123 (19 LOC)server/calculator.js
function peCompression(company) {
// Use pre-computed if available
if (company.peCompression) return company.peCompression;
const t = company.trailingPe;
const r = company.runRatePe;
const f = company.forwardPe;
if (t === null && r === null && f === null) return null;
return {
trailingPe: t,
runRatePe: r,
forwardPe: f,
trailingToRunRate: (t !== null && r !== null) ? Math.round((t - r) * 100) / 100 : null,
runRateToForward: (r !== null && f !== null) ? Math.round((r - f) * 100) / 100 : null,
totalCompression: (t !== null && f !== null) ? Math.round((t - f) * 100) / 100 : null,
};
}computeAllMetrics function · javascript · L128-L137 (10 LOC)server/calculator.js
function computeAllMetrics(company) {
return {
ticker: company.ticker,
momentum: sequentialMomentum(company),
gav: growthAdjustedValuation(company),
operatingLeverage: operatingLeverage(company),
distanceFromHigh: distanceFrom52WeekHigh(company),
peCompression: peCompression(company),
};
}enrichCompanies function · javascript · L142-L147 (6 LOC)server/calculator.js
function enrichCompanies(companies) {
return companies.map(company => ({
...company,
calculated: computeAllMetrics(company),
}));
}loadPortfolioConfig function · javascript · L34-L44 (11 LOC)server/dataLoader.js
function loadPortfolioConfig() {
try {
const raw = fs.readFileSync(PORTFOLIO_PATH, 'utf-8');
const config = JSON.parse(raw);
portfolioHoldings = (config.holdings || []).map(t => t.toUpperCase());
console.log(`[dataLoader] Portfolio: ${portfolioHoldings.length} holdings — ${portfolioHoldings.join(', ')}`);
} catch (err) {
console.warn('[dataLoader] Could not read portfolio.json, showing all companies:', err.message);
portfolioHoldings = [];
}
}scanReportsDirectory function · javascript · L46-L59 (14 LOC)server/dataLoader.js
function scanReportsDirectory() {
if (!fs.existsSync(REPORTS_DIR)) {
console.error(`[dataLoader] Reports directory not found: ${REPORTS_DIR}`);
return [];
}
const entries = fs.readdirSync(REPORTS_DIR, { withFileTypes: true });
const companyDirs = entries
.filter(e => e.isDirectory() && e.name !== 'validation')
.map(e => e.name);
console.log(`[dataLoader] Found ${companyDirs.length} company directories in ${REPORTS_DIR}`);
return companyDirs;
}findJsonFile function · javascript · L61-L79 (19 LOC)server/dataLoader.js
function findJsonFile(dirPath, ticker) {
const files = fs.readdirSync(dirPath);
const tickerLower = ticker.toLowerCase();
// Prefer dashboard_metrics.json (unified format with evaluation data)
const metricsFile = files.find(f =>
f.toLowerCase() === `${tickerLower}_dashboard_metrics.json`
);
if (metricsFile) {
console.log(`[dataLoader] ${ticker}: Using dashboard_metrics.json`);
return path.join(dirPath, metricsFile);
}
// Fall back to company_data.json (legacy format)
const jsonFile = files.find(f =>
f.toLowerCase() === `${tickerLower}_company_data.json`
);
return jsonFile ? path.join(dirPath, jsonFile) : null;
}findLatestMarkdown function · javascript · L81-L93 (13 LOC)server/dataLoader.js
function findLatestMarkdown(dirPath, ticker) {
const files = fs.readdirSync(dirPath);
// Find all markdown files matching TICKER_YYYYMMDD.md pattern
const mdFiles = files
.filter(f => {
const upper = f.toUpperCase();
return upper.startsWith(ticker.toUpperCase() + '_') && upper.endsWith('.MD');
})
.sort()
.reverse(); // newest first by filename date
return mdFiles.length > 0 ? path.join(dirPath, mdFiles[0]) : null;
}backfillQuarterEndDates function · javascript · L100-L145 (46 LOC)server/dataLoader.js
function backfillQuarterEndDates(normalized, dirPath, ticker, primaryJsonPath) {
const hist = normalized.quarterlyHistory;
if (!hist || hist.length === 0) return;
// Check if any entries already have calendarQuarter — if so, nothing to do
if (hist.some(q => q.calendarQuarter)) return;
// Check if any quarter labels suggest a fiscal year (contain "FY")
const hasFiscalQuarters = hist.some(q => q.quarter && q.quarter.includes('FY'));
if (!hasFiscalQuarters) return;
// Try to find the company_data.json as a secondary source
const tickerLower = ticker.toLowerCase();
const companyDataPath = path.join(dirPath, `${tickerLower}_company_data.json`);
if (companyDataPath === primaryJsonPath || !fs.existsSync(companyDataPath)) return;
try {
const raw = JSON.parse(fs.readFileSync(companyDataPath, 'utf-8'));
const cdHist = raw.quantitative?.quarterly_history;
if (!Array.isArray(cdHist)) return;
// Build a map of quarter label → quarter_end date
const eRepobility — same analyzer, your code, free for public repos · /scan/
loadCompany function · javascript · L147-L199 (53 LOC)server/dataLoader.js
function loadCompany(dirPath, ticker) {
const result = {
ticker: ticker.toUpperCase(),
normalized: null,
analysis: null,
rawMarkdown: null,
hasJson: false,
hasMd: false,
errors: [],
};
// Load JSON
const jsonPath = findJsonFile(dirPath, ticker);
if (jsonPath) {
try {
const raw = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'));
result.normalized = normalizeCompany(raw);
result.hasJson = true;
// If dashboard_metrics was used (no quarter_end dates), backfill from company_data
backfillQuarterEndDates(result.normalized, dirPath, ticker, jsonPath);
} catch (err) {
result.errors.push(`JSON parse error: ${err.message}`);
console.warn(`[dataLoader] ${ticker}: JSON error — ${err.message}`);
}
}
// Load Markdown
const mdPath = findLatestMarkdown(dirPath, ticker);
if (mdPath) {
try {
const mdText = fs.readFileSync(mdPath, 'utf-8');
result.rawMarkdown = mdText;
result.analysimergeMarkdownIntoNormalized function · javascript · L201-L264 (64 LOC)server/dataLoader.js
function mergeMarkdownIntoNormalized(norm, analysis) {
// Verdict & conviction: markdown fills gaps only (dashboard_metrics is primary)
if (!norm.verdict && analysis.verdict) norm.verdict = analysis.verdict;
if (!norm.convictionScore && analysis.convictionScore) norm.convictionScore = analysis.convictionScore;
// Fill in P/E values from markdown if missing in JSON
if (analysis.peValues) {
if (!norm.trailingPe && analysis.peValues.trailingPe) norm.trailingPe = analysis.peValues.trailingPe;
if (!norm.runRatePe && analysis.peValues.runRatePe) norm.runRatePe = analysis.peValues.runRatePe;
if (!norm.forwardPe && analysis.peValues.forwardPe) norm.forwardPe = analysis.peValues.forwardPe;
if (!norm.normalizedPe && analysis.peValues.normalizedPe) norm.normalizedPe = analysis.peValues.normalizedPe;
if (!norm.priceToSales && analysis.peValues.priceToSales) norm.priceToSales = analysis.peValues.priceToSales;
}
// Fill financial metrics from markdown
const fin buildFromMarkdownOnly function · javascript · L266-L300 (35 LOC)server/dataLoader.js
function buildFromMarkdownOnly(analysis, ticker) {
return {
ticker: ticker.toUpperCase(),
companyName: ticker.toUpperCase(),
fetchDate: analysis.date,
price: analysis.price,
marketCapMil: null,
trailingPe: analysis.peValues?.trailingPe || null,
runRatePe: analysis.peValues?.runRatePe || null,
forwardPe: analysis.peValues?.forwardPe || null,
normalizedPe: analysis.peValues?.normalizedPe || null,
priceToSales: analysis.peValues?.priceToSales || null,
revenueRecentMil: null,
revenueYoyPct: analysis.financials?.revenueYoyPct || null,
revenueQoqPct: analysis.financials?.revenueQoqPct || null,
grossMarginPct: analysis.financials?.grossMarginPct || null,
ebitdaMarginPct: analysis.financials?.ebitdaMarginPct || null,
quarterlyHistory: analysis.quarterlyHistory || [],
verdict: analysis.verdict,
convictionScore: analysis.convictionScore,
confidenceLevel: null,
keyStrengths: [],
keyConcerns: [],
saulSummary:page 1 / 3next ›