import { existsSync } from 'node:fs'; import { join } from 'node:path'; import type { TaxonomyHydrationInput, TaxonomyHydrationResult } from '@/lib/server/taxonomy/types'; import { withRetry } from '@/lib/server/utils/retry'; function candidateBinaryPaths() { return [ process.env.FISCAL_XBRL_BIN?.trim(), join(process.cwd(), 'bin', 'fiscal-xbrl'), join(process.cwd(), 'rust', 'target', 'release', 'fiscal-xbrl'), join(process.cwd(), 'rust', 'target', 'debug', 'fiscal-xbrl') ].filter((value): value is string => typeof value === 'string' && value.length > 0); } export function resolveFiscalXbrlBinary() { const resolved = candidateBinaryPaths().find((path) => existsSync(path)); if (!resolved) { throw new Error('Rust XBRL sidecar binary is required but was not found. Set FISCAL_XBRL_BIN or build `fiscal-xbrl` under rust/target.'); } return resolved; } export async function hydrateFilingTaxonomySnapshotFromSidecar( input: TaxonomyHydrationInput ): Promise { return withRetry(() => hydrateFromSidecarImpl(input)); } async function hydrateFromSidecarImpl(input: TaxonomyHydrationInput): Promise { const binary = resolveFiscalXbrlBinary(); const timeoutMs = Math.max(Number(process.env.XBRL_ENGINE_TIMEOUT_MS ?? 45_000), 1_000); const command = [binary, 'hydrate-filing']; const requestBody = JSON.stringify({ filingId: input.filingId, ticker: input.ticker, cik: input.cik, accessionNumber: input.accessionNumber, filingDate: input.filingDate, filingType: input.filingType, filingUrl: input.filingUrl, primaryDocument: input.primaryDocument, cacheDir: process.env.FISCAL_XBRL_CACHE_DIR ?? join(process.cwd(), '.cache', 'xbrl') }); const child = Bun.spawn(command, { stdin: 'pipe', stdout: 'pipe', stderr: 'pipe', env: { ...process.env } }); child.stdin.write(new TextEncoder().encode(requestBody)); child.stdin.end(); const timeout = setTimeout(() => { child.kill(); }, timeoutMs); try { const [stdout, stderr, exitCode] = await Promise.all([ new Response(child.stdout).text(), new Response(child.stderr).text(), child.exited ]); if (stderr.trim().length > 0) { console.warn(`[fiscal-xbrl] ${stderr.trim()}`); } if (exitCode !== 0) { throw new Error(`Rust XBRL sidecar failed with exit code ${exitCode}: ${stderr.trim() || stdout.trim() || 'no error output'}`); } return JSON.parse(stdout) as TaxonomyHydrationResult; } finally { clearTimeout(timeout); } }