const path = require('path'); const protoLoader = require('@grpc/proto-loader'); const grpc = require('@grpc/grpc-js'); const fs = require('fs'); /** * gRPC proto 로딩 결과를 캐싱하기 위한 변수 */ let _cache = null; const findProtoPath = () => { const attemptedPaths = []; // 1. Try explicit environment variable if (process.env.KORD_PROTO_PATH) { if (fs.existsSync(process.env.KORD_PROTO_PATH)) return process.env.KORD_PROTO_PATH; attemptedPaths.push(`Env: ${process.env.KORD_PROTO_PATH}`); } // 2. Try standard __dirname (most reliable for direct node/tsx runs) const dirnamePath = path.resolve(__dirname, 'kord.proto'); if (fs.existsSync(dirnamePath)) return dirnamePath; attemptedPaths.push(`Dirname: ${dirnamePath}`); // 3. Try monorepo root via process.cwd() const rootPath = path.resolve(process.cwd(), 'packages/grpc-contracts/src/kord.proto'); if (fs.existsSync(rootPath)) return rootPath; attemptedPaths.push(`CWD Root: ${rootPath}`); // 4. Try relative from apps/ (Next.js context) const relativePath = path.resolve(process.cwd(), '../../packages/grpc-contracts/src/kord.proto'); if (fs.existsSync(relativePath)) return relativePath; attemptedPaths.push(`Relative: ${relativePath}`); // 5. Try shared node_modules depth (monorepo artifacts) const nmPath = path.resolve(process.cwd(), 'node_modules/@kord/grpc-contracts/src/kord.proto'); if (fs.existsSync(nmPath)) return nmPath; attemptedPaths.push(`NodeModules: ${nmPath}`); return { error: true, attemptedPaths }; }; const initialize = () => { if (_cache) return _cache; // browser환경이나 분석 단계에서 fs가 없을 경우를 대비하여 체크 (Turbopack 대응) if (!fs || !fs.existsSync) { return { kordProto: null, protoPath: null }; } const result = findProtoPath(); if (result.error) { // 런타임에 에러가 발생하도록 경고를 출력하되, 빌드/분석 단계에서는 치명적 에러가 되지 않도록 함 console.warn('⚠️ [grpc-contracts] Failed to locate kord.proto during initialization.'); return { kordProto: null, protoPath: null }; } const PROTO_PATH = result; let packageDefinition; try { packageDefinition = protoLoader.loadSync(PROTO_PATH, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true, }); } catch (error) { console.error(`Failed to load gRPC proto from: ${PROTO_PATH}`); throw error; } const protoDescriptor = grpc.loadPackageDefinition(packageDefinition); _cache = { kordProto: protoDescriptor.kord, protoPath: PROTO_PATH }; return _cache; }; module.exports = { // getter를 사용하여 실제 접근 시점에 로딩되도록 함 (Lazy Initialization) get kordProto() { return initialize().kordProto; }, get protoPath() { return initialize().protoPath; } };