nucleus-core-ts 0.9.962 → 0.9.963

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/.build-ok CHANGED
@@ -1 +1 @@
1
- 0.9.962
1
+ 0.9.963
package/dist/index.js CHANGED
@@ -84,7 +84,7 @@ and limitations under the License.
84
84
  AND state = 'active'
85
85
  AND query_start IS NOT NULL
86
86
  AND now() - query_start > interval '${thresholdMs} milliseconds'`);result.slowQueries=this.toNumber(rows[0]?.slow)}catch{}return result}parseThresholdMs(threshold){let match=threshold.match(/^(\d+)(ms|s|m)?$/);if(!match||!match[1])return 100;let value=Number.parseInt(match[1],10);switch(match[2]){case"s":return value*1000;case"m":return value*60*1000;default:return value}}}class RedisCollector{getClient;constructor(getClient){this.getClient=getClient}async collect(){let client=this.getClient();if(!client)return null;try{let info=await client.info(),fields=this.parseInfo(info),hits=this.toNumber(fields.keyspace_hits),misses=this.toNumber(fields.keyspace_misses),totalLookups=hits+misses;return{memory:{used:this.toNumber(fields.used_memory),peak:this.toNumber(fields.used_memory_peak)},connections:{connected:this.toNumber(fields.connected_clients)},operations:{totalCommands:this.toNumber(fields.total_commands_processed),opsPerSec:this.toNumber(fields.instantaneous_ops_per_sec)},hitRate:totalLookups>0?Math.round(hits/totalLookups*100*100)/100:0}}catch{return null}}parseInfo(info){let fields={};for(let line of info.split(`
87
- `)){let trimmed=line.trim();if(!trimmed||trimmed.startsWith("#"))continue;let separatorIndex=trimmed.indexOf(":");if(separatorIndex===-1)continue;let key=trimmed.slice(0,separatorIndex),value=trimmed.slice(separatorIndex+1);fields[key]=value}return fields}toNumber(value){if(!value)return 0;let num=Number.parseFloat(value);return Number.isNaN(num)?0:num}}function parseCgroupLimit(raw){if(!raw)return null;let trimmed=raw.trim();if(trimmed===""||trimmed==="max")return null;let value=Number(trimmed);if(!Number.isFinite(value)||value<=0)return null;if(value>=1152921504606847000)return null;return value}function runtimeMemoryAdvice(input){let{cgroupLimit,hostTotal,forcedRamSize}=input;if(cgroupLimit===null||cgroupLimit<=0)return null;if(cgroupLimit>=hostTotal)return null;let recommended=Math.floor(cgroupLimit/4);if(forcedRamSize!==null&&forcedRamSize>0&&forcedRamSize<=recommended)return null;return{cgroupLimit,hostTotal,recommended}}function parseCgroupUsage(raw){if(!raw)return null;let value=Number(raw.trim());return Number.isFinite(value)&&value>=0?value:null}function parseInactiveFile(raw){if(!raw)return 0;for(let line of raw.split(`
87
+ `)){let trimmed=line.trim();if(!trimmed||trimmed.startsWith("#"))continue;let separatorIndex=trimmed.indexOf(":");if(separatorIndex===-1)continue;let key=trimmed.slice(0,separatorIndex),value=trimmed.slice(separatorIndex+1);fields[key]=value}return fields}toNumber(value){if(!value)return 0;let num=Number.parseFloat(value);return Number.isNaN(num)?0:num}}function parseCgroupLimit(raw){if(!raw)return null;let trimmed=raw.trim();if(trimmed===""||trimmed==="max")return null;let value=Number(trimmed);if(!Number.isFinite(value)||value<=0)return null;if(value>=1152921504606847000)return null;return value}function runtimeMemoryAdvice(input){let{cgroupLimit,hostTotal,forcedRamSize}=input;if(cgroupLimit===null||cgroupLimit<=0)return null;if(cgroupLimit>=hostTotal)return null;return{cgroupLimit,hostTotal}}function parseCgroupUsage(raw){if(!raw)return null;let value=Number(raw.trim());return Number.isFinite(value)&&value>=0?value:null}function parseInactiveFile(raw){if(!raw)return 0;for(let line of raw.split(`
88
88
  `)){let[key,value]=line.trim().split(/\s+/);if(key==="inactive_file"||key==="total_inactive_file"){let parsed=Number(value);if(Number.isFinite(parsed)&&parsed>=0)return parsed}}return 0}function parseMemAvailable(raw){if(!raw)return null;for(let line of raw.split(`
89
89
  `)){let match=line.match(/^MemAvailable:\s+(\d+)\s+kB/);if(match?.[1])return Number(match[1])*1024}return null}function percent(used,total){if(!Number.isFinite(total)||total<=0)return 0;return Math.round(used/total*100*100)/100}function resolveMemory(sources){let{limit,current,inactiveFile,hostTotal,hostAvailable,hostFree,process:process2}=sources;if(limit!==null&&current!==null&&limit<hostTotal&&limit!==null&&current!==null){let workingSet=Math.max(0,current-inactiveFile);return{total:limit,used:workingSet,free:Math.max(0,limit-workingSet),usagePercent:percent(workingSet,limit),rss:process2.rss,heapUsed:process2.heapUsed,heapTotal:process2.heapTotal,external:process2.external,arrayBuffers:process2.arrayBuffers,scope:"container"}}let free=hostAvailable??hostFree,used=Math.max(0,hostTotal-free);return{total:hostTotal,used,free,usagePercent:percent(used,hostTotal),rss:process2.rss,heapUsed:process2.heapUsed,heapTotal:process2.heapTotal,external:process2.external,arrayBuffers:process2.arrayBuffers,scope:"host"}}import*as fs3 from"fs";import*as os from"os";function readCgroupLimitBytes(){let read=(path2)=>{try{return fs3.readFileSync(path2,"utf-8")}catch{return null}};return parseCgroupLimit(read("/sys/fs/cgroup/memory.max")??read("/sys/fs/cgroup/memory/memory.limit_in_bytes"))}function readRuntimeMemoryAdvice(){let read=(path2)=>{try{return fs3.readFileSync(path2,"utf-8")}catch{return null}},limitRaw=read("/sys/fs/cgroup/memory.max")??read("/sys/fs/cgroup/memory/memory.limit_in_bytes"),forced=Number(process.env.BUN_JSC_forceRAMSize);return runtimeMemoryAdvice({cgroupLimit:parseCgroupLimit(limitRaw),hostTotal:os.totalmem(),forcedRamSize:Number.isFinite(forced)&&forced>0?forced:null})}class SystemCollector{config;lastCpuInfo=null;constructor(config){this.config=config}async collect(){if(!this.config?.enabled)return null;let metrics={cpu:{usage:0,cores:0},memory:{total:0,used:0,free:0,usagePercent:0,rss:0,heapUsed:0,heapTotal:0,external:0,arrayBuffers:0,scope:"host"},disk:{total:0,used:0,free:0,usagePercent:0},network:{bytesIn:0,bytesOut:0},process:{uptime:0,pid:0,eventLoopLag:0}};if(this.config.metrics?.cpu!==!1)metrics.cpu=this.collectCpu();if(this.config.metrics?.memory!==!1)metrics.memory=this.collectMemory();if(this.config.metrics?.disk!==!1)metrics.disk=await this.collectDisk();if(this.config.metrics?.network)metrics.network=this.collectNetwork();if(this.config.metrics?.process!==!1)metrics.process=await this.collectProcess();return metrics}collectCpu(){let cpus2=os.cpus(),cores=cpus2.length,idle=0,total=0;for(let cpu of cpus2)idle+=cpu.times.idle,total+=cpu.times.user+cpu.times.nice+cpu.times.sys+cpu.times.idle+cpu.times.irq;let usage=0;if(this.lastCpuInfo){let idleDiff=idle-this.lastCpuInfo.idle,totalDiff=total-this.lastCpuInfo.total;usage=totalDiff>0?Math.round((1-idleDiff/totalDiff)*100*100)/100:0}return this.lastCpuInfo={idle,total},{usage,cores}}read(path2){try{return fs3.readFileSync(path2,"utf-8")}catch{return null}}collectMemory(){let memUsage=process.memoryUsage(),limitRaw=this.read("/sys/fs/cgroup/memory.max")??this.read("/sys/fs/cgroup/memory/memory.limit_in_bytes"),usageRaw=this.read("/sys/fs/cgroup/memory.current")??this.read("/sys/fs/cgroup/memory/memory.usage_in_bytes"),statRaw=this.read("/sys/fs/cgroup/memory.stat")??this.read("/sys/fs/cgroup/memory/memory.stat");return resolveMemory({limit:parseCgroupLimit(limitRaw),current:parseCgroupUsage(usageRaw),inactiveFile:parseInactiveFile(statRaw),hostTotal:os.totalmem(),hostAvailable:parseMemAvailable(this.read("/proc/meminfo")),hostFree:os.freemem(),process:{rss:memUsage.rss,heapUsed:memUsage.heapUsed,heapTotal:memUsage.heapTotal,external:memUsage.external,arrayBuffers:memUsage.arrayBuffers}})}async collectDisk(){try{let stats=fs3.statfsSync("/"),total=stats.blocks*stats.bsize,free=stats.bfree*stats.bsize,used=total-free,usagePercent=Math.round(used/total*100*100)/100;return{total,used,free,usagePercent}}catch{return{total:0,used:0,free:0,usagePercent:0}}}collectNetwork(){try{let content=fs3.readFileSync("/proc/net/dev","utf-8"),bytesIn=0,bytesOut=0;for(let line of content.split(`
90
90
  `).slice(2)){let[namePart,dataPart]=line.split(":");if(!namePart||!dataPart)continue;if(namePart.trim()==="lo")continue;let fields=dataPart.trim().split(/\s+/);bytesIn+=Number.parseInt(fields[0]??"0",10)||0,bytesOut+=Number.parseInt(fields[8]??"0",10)||0}return{bytesIn,bytesOut}}catch{return{bytesIn:0,bytesOut:0}}}async collectProcess(){let uptime=process.uptime(),pid=process.pid,lagStart=Date.now(),eventLoopLag=await new Promise((resolve)=>{setImmediate(()=>{resolve(Date.now()-lagStart)})});return{uptime,pid,eventLoopLag}}}var init_SystemCollector=()=>{};function peakContext(snapshot){let app=snapshot.application,busiest=Object.entries(app?.requests?.byEndpoint??{}).sort((a,b)=>b[1]-a[1])[0],tags={};if(busiest)tags.busiestEndpoint=busiest[0],tags.busiestEndpointCount=String(busiest[1]);if(app?.requests?.perMinute!==void 0)tags.requestsPerMinute=String(app.requests.perMinute);if(app?.responseTime?.p95!==void 0)tags.p95=String(app.responseTime.p95);if(app?.errors?.rate!==void 0)tags.errorRate=String(app.errors.rate);if(snapshot.system?.cpu?.usage!==void 0)tags.cpu=String(snapshot.system.cpu.usage);if(snapshot.system?.memory?.usagePercent!==void 0)tags.memory=String(snapshot.system.memory.usagePercent);if(snapshot.database?.connections?.active!==void 0)tags.dbConnections=String(snapshot.database.connections.active);return tags}function newPeaks(snapshot,previous,timestamp){let peaks=new Map(previous),points=[],tags=peakContext(snapshot);for(let watched of WATCHED_PEAKS){let value=watched.of(snapshot);if(value===void 0||!Number.isFinite(value))continue;let before=peaks.get(watched.name);if(before!==void 0&&value<=before)continue;peaks.set(watched.name,value),points.push({timestamp,metricType:"peak",metricName:watched.name,value,tags})}return{points,peaks}}var WATCHED_PEAKS;var init_peaks=__esm(()=>{WATCHED_PEAKS=[{name:"requests.perMinute",of:(s)=>s.application?.requests?.perMinute},{name:"responseTime.p95",of:(s)=>s.application?.responseTime?.p95},{name:"errors.rate",of:(s)=>s.application?.errors?.rate},{name:"cpu.usage",of:(s)=>s.system?.cpu?.usage},{name:"memory.usagePercent",of:(s)=>s.system?.memory?.usagePercent},{name:"database.connections.active",of:(s)=>s.database?.connections?.active}]});import{randomUUID}from"crypto";import*as os2 from"os";class LiveMonitoringService{store;memoryInterval=null;cpuInterval=null;lastCpuInfo=null;isRunning=!1;constructor(config){let merged={...DEFAULT_LIVE_CONFIG,...config};this.store={requests:[],configs:{logMemory:merged.logMemory,logCpu:merged.logCpu,logDapr:merged.logDapr,logWebSocket:merged.logWebSocket,cpuLogInterval:merged.cpuLogInterval,memoryLogInterval:merged.memoryLogInterval},logs:{memory:[],cpu:[],dapr:[],ws:[]},logLimits:{memory:merged.memoryLogLimit,cpu:merged.cpuLogLimit,dapr:merged.daprLogLimit,ws:merged.wsLogLimit,request:merged.requestLogLimit},worker:{pid:process.pid,workerId:null,memory:null,cpu:null,updatedAt:Date.now()},allWorkers:[],daprEvents:[],wsEvents:[]}}start(){if(this.isRunning)return;if(this.isRunning=!0,this.store.configs.logMemory)this.startMemoryCollector();if(this.store.configs.logCpu)this.startCpuCollector()}stop(){if(!this.isRunning)return;if(this.isRunning=!1,this.memoryInterval)clearInterval(this.memoryInterval),this.memoryInterval=null;if(this.cpuInterval)clearInterval(this.cpuInterval),this.cpuInterval=null}startMemoryCollector(){if(this.memoryInterval)clearInterval(this.memoryInterval);let collect=()=>{if(!this.store.configs.logMemory)return;let mem=process.memoryUsage(),entry={timestamp:Date.now(),rss:mem.rss,heapUsed:mem.heapUsed,heapTotal:mem.heapTotal};if(this.store.logs.memory.push(entry),this.store.logs.memory.length>this.store.logLimits.memory*2)this.store.logs.memory=this.store.logs.memory.slice(-this.store.logLimits.memory);this.store.worker.memory=entry,this.store.worker.updatedAt=Date.now()};collect(),this.memoryInterval=setInterval(collect,this.store.configs.memoryLogInterval)}startCpuCollector(){if(this.cpuInterval)clearInterval(this.cpuInterval);let collect=()=>{if(!this.store.configs.logCpu)return;let cpus3=os2.cpus(),userTime=0,sysTime=0,idle=0,total=0;for(let cpu of cpus3)userTime+=cpu.times.user,sysTime+=cpu.times.sys,idle+=cpu.times.idle,total+=cpu.times.user+cpu.times.nice+cpu.times.sys+cpu.times.idle+cpu.times.irq;let userPercent=0,sysPercent=0;if(this.lastCpuInfo){let totalDiff=total-this.lastCpuInfo.total,idleDiff=idle-this.lastCpuInfo.idle;if(totalDiff>0){let activeDiff=totalDiff-idleDiff;userPercent=Math.round((userTime-0)/(activeDiff||1)*100*100)/100,sysPercent=Math.round((sysTime-0)/(activeDiff||1)*100*100)/100;let totalActive=Math.round((1-idleDiff/totalDiff)*100*100)/100;userPercent=Math.round(totalActive*0.7*100)/100,sysPercent=Math.round(totalActive*0.3*100)/100}}this.lastCpuInfo={idle,total};let entry={timestamp:Date.now(),user:userPercent,system:sysPercent};if(this.store.logs.cpu.push(entry),this.store.logs.cpu.length>this.store.logLimits.cpu*2)this.store.logs.cpu=this.store.logs.cpu.slice(-this.store.logLimits.cpu);this.store.worker.cpu=entry,this.store.worker.updatedAt=Date.now()};collect(),this.cpuInterval=setInterval(collect,this.store.configs.cpuLogInterval)}recordRequest(request){if(this.store.requests.push(request),this.store.requests.length>this.store.logLimits.request*2)this.store.requests=this.store.requests.slice(-this.store.logLimits.request)}recordDaprEvent(type,details){if(!this.store.configs.logDapr)return;let event={id:randomUUID(),type,timestamp:Date.now(),...details};if(this.store.logs.dapr.push(event),this.store.daprEvents.push(event),this.store.logs.dapr.length>this.store.logLimits.dapr*2)this.store.logs.dapr=this.store.logs.dapr.slice(-this.store.logLimits.dapr);if(this.store.daprEvents.length>this.store.logLimits.dapr*2)this.store.daprEvents=this.store.daprEvents.slice(-this.store.logLimits.dapr)}recordWsEvent(type,details){if(!this.store.configs.logWebSocket)return;let event={id:randomUUID(),type,timestamp:Date.now(),...details};if(this.store.logs.ws.push(event),this.store.wsEvents.push(event),this.store.logs.ws.length>this.store.logLimits.ws*2)this.store.logs.ws=this.store.logs.ws.slice(-this.store.logLimits.ws);if(this.store.wsEvents.length>this.store.logLimits.ws*2)this.store.wsEvents=this.store.wsEvents.slice(-this.store.logLimits.ws)}getSnapshot(){return{memory:this.store.logs.memory.slice(-this.store.logLimits.memory),cpu:this.store.logs.cpu.slice(-this.store.logLimits.cpu),requests:this.store.requests.slice(-this.store.logLimits.request),dapr:this.store.logs.dapr.slice(-this.store.logLimits.dapr),ws:this.store.logs.ws.slice(-this.store.logLimits.ws),workers:this.store.allWorkers.length?this.store.allWorkers:[this.store.worker],logLimits:{...this.store.logLimits},configs:{...this.store.configs}}}getUpdatesSince(timestamps){let memoryUpdates=this.store.logs.memory.filter((m)=>m.timestamp>timestamps.memory),cpuUpdates=this.store.logs.cpu.filter((c)=>c.timestamp>timestamps.cpu),requestUpdates=this.store.requests.filter((r)=>r.timestamp>timestamps.request),daprUpdates=this.store.logs.dapr.filter((d)=>d.timestamp>timestamps.dapr),wsUpdates=this.store.logs.ws.filter((w)=>w.timestamp>timestamps.ws);if(!(memoryUpdates.length>0||cpuUpdates.length>0||requestUpdates.length>0||daprUpdates.length>0||wsUpdates.length>0))return null;return{memory:memoryUpdates,cpu:cpuUpdates,requests:requestUpdates,dapr:daprUpdates,ws:wsUpdates,timestamp:Date.now()}}getLogs(){return{memory:this.store.logs.memory,cpu:this.store.logs.cpu,requests:this.store.requests,dapr:this.store.logs.dapr,ws:this.store.logs.ws,daprEvents:this.store.daprEvents,wsEvents:this.store.wsEvents,configs:{logMemory:this.store.configs.logMemory,logCpu:this.store.configs.logCpu,logDapr:this.store.configs.logDapr,logWebSocket:this.store.configs.logWebSocket},limits:{...this.store.logLimits}}}getSettings(){return{configs:{...this.store.configs},logLimits:{...this.store.logLimits}}}changeSettings(payload){if(payload.logMemory!==void 0)this.store.configs.logMemory=payload.logMemory;if(payload.logCpu!==void 0)this.store.configs.logCpu=payload.logCpu;if(payload.logDapr!==void 0)this.store.configs.logDapr=payload.logDapr;if(payload.logWebSocket!==void 0)this.store.configs.logWebSocket=payload.logWebSocket;if(payload.cpuLogInterval!==void 0){if(this.store.configs.cpuLogInterval=payload.cpuLogInterval,this.isRunning&&this.store.configs.logCpu)this.startCpuCollector()}if(payload.memoryLogInterval!==void 0){if(this.store.configs.memoryLogInterval=payload.memoryLogInterval,this.isRunning&&this.store.configs.logMemory)this.startMemoryCollector()}if(payload.memoryLogLimit!==void 0)this.store.logLimits.memory=payload.memoryLogLimit;if(payload.cpuLogLimit!==void 0)this.store.logLimits.cpu=payload.cpuLogLimit;if(payload.daprLogLimit!==void 0)this.store.logLimits.dapr=payload.daprLogLimit;if(payload.wsLogLimit!==void 0)this.store.logLimits.ws=payload.wsLogLimit;if(payload.requestLogLimit!==void 0)this.store.logLimits.request=payload.requestLogLimit;return{message:"Settings updated successfully",configs:{...this.store.configs},logLimits:{...this.store.logLimits}}}getStore(){return this.store}isEnabled(){return this.isRunning}}var DEFAULT_LIVE_CONFIG;var init_LiveMonitoringService=__esm(()=>{DEFAULT_LIVE_CONFIG={enabled:!0,logMemory:!0,logCpu:!0,logDapr:!0,logWebSocket:!0,memoryLogInterval:1000,cpuLogInterval:1000,memoryLogLimit:100,cpuLogLimit:100,daprLogLimit:100,wsLogLimit:100,requestLogLimit:100,streamInterval:150}});class MonitoringService{redis;logger;config;appId;flushToDb;systemCollector;applicationCollector;databaseCollector=null;redisCollector=null;alertService;collectInterval=null;flushInterval=null;pendingMetrics=[];isRunning=!1;peaks=new Map;peaksSeeded=!1;dbQuery;loadPeaks;constructor(deps){if(this.redis=deps.redis,this.logger=deps.logger,this.config=this.mergeConfig(deps.config),this.appId=deps.appId,this.flushToDb=deps.flushToDb,this.dbQuery=deps.dbQuery,this.loadPeaks=deps.loadPeaks,this.config.database.enabled)if(deps.dbQuery)this.databaseCollector=new DatabaseCollector(this.config.database,deps.dbQuery);else this.logger.warn("[Monitoring] monitoring.database is enabled but no database query executor is wired \u2014 database metrics are skipped");if(this.config.redis.enabled)if(deps.redis.getDirectClient())this.redisCollector=new RedisCollector(()=>deps.redis.getDirectClient());else this.logger.warn("[Monitoring] monitoring.redis is enabled but Redis runs in Dapr mode \u2014 INFO is unavailable, Redis metrics are skipped");if(this.config.persistence.enabled&&!deps.flushToDb)this.logger.warn("[Monitoring] monitoring.persistence is enabled but no flushToDb handler is wired \u2014 metrics are kept in Redis only");this.systemCollector=new SystemCollector(this.config.system),this.applicationCollector=new ApplicationCollector(this.config.application),this.alertService=new AlertService({logger:deps.logger,getEmailService:deps.getEmailService,config:this.config,appId:deps.appId})}mergeConfig(config){return{enabled:config.enabled??DEFAULT_CONFIG3.enabled,system:{enabled:config.system?.enabled??DEFAULT_CONFIG3.system.enabled,collectInterval:config.system?.collectInterval??DEFAULT_CONFIG3.system.collectInterval,metrics:{cpu:config.system?.metrics?.cpu??DEFAULT_CONFIG3.system.metrics.cpu,memory:config.system?.metrics?.memory??DEFAULT_CONFIG3.system.metrics.memory,disk:config.system?.metrics?.disk??DEFAULT_CONFIG3.system.metrics.disk,network:config.system?.metrics?.network??DEFAULT_CONFIG3.system.metrics.network,process:config.system?.metrics?.process??DEFAULT_CONFIG3.system.metrics.process}},application:{enabled:config.application?.enabled??DEFAULT_CONFIG3.application.enabled,metrics:{requests:config.application?.metrics?.requests??DEFAULT_CONFIG3.application.metrics.requests,responseTime:config.application?.metrics?.responseTime??DEFAULT_CONFIG3.application.metrics.responseTime,errors:config.application?.metrics?.errors??DEFAULT_CONFIG3.application.metrics.errors,rateLimits:config.application?.metrics?.rateLimits??DEFAULT_CONFIG3.application.metrics.rateLimits}},database:{enabled:config.database?.enabled??DEFAULT_CONFIG3.database.enabled,metrics:{connections:config.database?.metrics?.connections??DEFAULT_CONFIG3.database.metrics.connections,queryTime:config.database?.metrics?.queryTime??DEFAULT_CONFIG3.database.metrics.queryTime,slowQueryThreshold:config.database?.metrics?.slowQueryThreshold??DEFAULT_CONFIG3.database.metrics.slowQueryThreshold}},redis:{enabled:config.redis?.enabled??DEFAULT_CONFIG3.redis.enabled},persistence:{enabled:config.persistence?.enabled??DEFAULT_CONFIG3.persistence.enabled,samples:config.persistence?.samples??DEFAULT_CONFIG3.persistence.samples,flushInterval:config.persistence?.flushInterval??DEFAULT_CONFIG3.persistence.flushInterval,retentionDays:config.persistence?.retentionDays??DEFAULT_CONFIG3.persistence.retentionDays},alerts:{enabled:config.alerts?.enabled??DEFAULT_CONFIG3.alerts.enabled,email:{enabled:config.alerts?.email?.enabled??DEFAULT_CONFIG3.alerts.email.enabled,recipients:config.alerts?.email?.recipients??DEFAULT_CONFIG3.alerts.email.recipients},thresholds:{cpuPercent:config.alerts?.thresholds?.cpuPercent??DEFAULT_CONFIG3.alerts.thresholds.cpuPercent,memoryPercent:config.alerts?.thresholds?.memoryPercent??DEFAULT_CONFIG3.alerts.thresholds.memoryPercent,diskPercent:config.alerts?.thresholds?.diskPercent??DEFAULT_CONFIG3.alerts.thresholds.diskPercent,errorRatePercent:config.alerts?.thresholds?.errorRatePercent??DEFAULT_CONFIG3.alerts.thresholds.errorRatePercent,responseTimeMs:config.alerts?.thresholds?.responseTimeMs??DEFAULT_CONFIG3.alerts.thresholds.responseTimeMs,rateLimitBlocksPerMinute:config.alerts?.thresholds?.rateLimitBlocksPerMinute??DEFAULT_CONFIG3.alerts.thresholds.rateLimitBlocksPerMinute},cooldown:config.alerts?.cooldown??DEFAULT_CONFIG3.alerts.cooldown}}}parseTimeToMs(time){let match=time.match(/^(\d+)(ms|s|m|h|d)$/);if(!match||!match[1]||!match[2])return 1e4;let value=parseInt(match[1],10);switch(match[2]){case"ms":return value;case"s":return value*1000;case"m":return value*60*1000;case"h":return value*60*60*1000;case"d":return value*24*60*60*1000;default:return 1e4}}start(){if(!this.config.enabled||this.isRunning)return;this.isRunning=!0,this.logger.info("[Monitoring] Starting monitoring service");let collectIntervalMs=this.parseTimeToMs(this.config.system.collectInterval);if(this.collectInterval=setInterval(()=>{this.collect()},collectIntervalMs),this.config.persistence.enabled&&this.flushToDb){let flushIntervalMs=this.parseTimeToMs(this.config.persistence.flushInterval);this.flushInterval=setInterval(()=>{this.flush()},flushIntervalMs)}this.collect()}stop(){if(!this.isRunning)return;if(this.isRunning=!1,this.logger.info("[Monitoring] Stopping monitoring service"),this.collectInterval)clearInterval(this.collectInterval),this.collectInterval=null;if(this.flushInterval)clearInterval(this.flushInterval),this.flushInterval=null;this.flush()}async collect(){let now=Date.now(),snapshot={timestamp:now};if(this.config.system.enabled){let systemMetrics=await this.systemCollector.collect();if(systemMetrics)snapshot.system=systemMetrics,this.addMetricPoints("system",systemMetrics,now)}if(this.config.application.enabled){let appMetrics=this.applicationCollector.collect();if(appMetrics)snapshot.application=appMetrics,this.addMetricPoints("application",appMetrics,now)}if(this.databaseCollector){let dbMetrics=await this.databaseCollector.collect();if(dbMetrics)snapshot.database=dbMetrics,this.addMetricPoints("database",dbMetrics,now)}if(this.redisCollector){let redisMetrics=await this.redisCollector.collect();if(redisMetrics)snapshot.redis=redisMetrics,this.addMetricPoints("redis",redisMetrics,now)}if(await this.recordPeaks(snapshot,now),await this.storeSnapshot(snapshot),this.config.alerts.enabled)await this.alertService.checkAndAlert(snapshot)}addMetricPoints(type,metrics,timestamp){if(!this.config.persistence.samples)return;let flatten=(obj,prefix="")=>{for(let key in obj){let value=obj[key],newKey=prefix?`${prefix}.${key}`:key;if(typeof value==="number")this.pendingMetrics.push({timestamp,metricType:type,metricName:newKey,value});else if(typeof value==="object"&&value!==null&&!Array.isArray(value))flatten(value,newKey)}};flatten(metrics)}async seedPeaks(){if(this.peaksSeeded)return;if(!this.loadPeaks)return;try{for(let row of await this.loadPeaks()){if(!row.metricName||this.peaks.has(row.metricName))continue;this.peaks.set(row.metricName,{value:row.value,at:row.at,context:row.context})}this.peaksSeeded=!0}catch(error){this.logger.warn(`[Monitoring] peak seed failed, peaks not recorded this cycle: ${error}`)}}async recordPeaks(snapshot,timestamp){if(await this.seedPeaks(),!this.peaksSeeded)return;let previous=new Map([...this.peaks].map(([name,peak])=>[name,peak.value])),{points}=newPeaks(snapshot,previous,timestamp);for(let point of points)this.peaks.set(point.metricName,{value:point.value,at:timestamp,context:point.tags}),this.pendingMetrics.push(point),this.logger.info(`[Monitoring] yeni tepe \u2014 ${point.metricName} = ${point.value}`);if(this.peaks.size>0)snapshot.peaks=Object.fromEntries(this.peaks)}async storeSnapshot(snapshot){let key=`monitoring:${this.appId}:latest`;await this.redis.create(key,snapshot,3600);let historyKey=`monitoring:${this.appId}:history`,historyResult=await this.redis.read(historyKey),history=historyResult.success&&historyResult.data?historyResult.data:[];history.push(snapshot);let oneHourAgo=Date.now()-3600000,filteredHistory=history.filter((s)=>s.timestamp>oneHourAgo);await this.redis.create(historyKey,filteredHistory,3600)}async flush(){if(this.pendingMetrics.length===0)return;if(!this.flushToDb)return;let metricsToFlush=[...this.pendingMetrics];this.pendingMetrics=[];try{await this.flushToDb(metricsToFlush),this.logger.debug(`[Monitoring] Flushed ${metricsToFlush.length} metrics to database`)}catch(error){this.logger.error(`[Monitoring] Failed to flush metrics: ${error}`),this.pendingMetrics=[...metricsToFlush,...this.pendingMetrics]}}recordRequest(params){if(!this.config.enabled||!this.config.application.enabled)return;this.applicationCollector.recordRequest(params)}recordRateLimitBlock(){if(!this.config.enabled||!this.config.application.enabled)return;this.applicationCollector.recordRateLimitBlock()}async getLatestSnapshot(){let key=`monitoring:${this.appId}:latest`,result=await this.redis.read(key);return result.success?result.data:null}async getHistory(minutes=60){let key=`monitoring:${this.appId}:history`,result=await this.redis.read(key);if(!result.success||!result.data)return[];let cutoff=Date.now()-minutes*60000;return result.data.filter((s)=>s.timestamp>cutoff)}getActiveAlerts(){return this.alertService.getActiveAlerts()}acknowledgeAlert(alertId){return this.alertService.acknowledgeAlert(alertId)}isEnabled(){return this.config.enabled}getConfig(){return this.config}}var DEFAULT_CONFIG3;var init_Monitoring=__esm(()=>{init_AlertService();init_SystemCollector();init_peaks();init_AlertService();init_LiveMonitoringService();DEFAULT_CONFIG3={enabled:!1,system:{enabled:!0,collectInterval:"10s",metrics:{cpu:!0,memory:!0,disk:!0,network:!1,process:!0}},application:{enabled:!0,metrics:{requests:!0,responseTime:!0,errors:!0,rateLimits:!0}},database:{enabled:!1,metrics:{connections:!0,queryTime:!0,slowQueryThreshold:"100ms"}},redis:{enabled:!1},persistence:{enabled:!0,samples:!1,flushInterval:"1m",retentionDays:30},alerts:{enabled:!1,email:{enabled:!1,recipients:[]},thresholds:{cpuPercent:80,memoryPercent:85,diskPercent:90,errorRatePercent:5,responseTimeMs:1000,rateLimitBlocksPerMinute:100},cooldown:"5m"}}});function snakeToCamel(name){return name.replace(/_([a-z])/g,(_,c)=>c.toUpperCase())}function resolveSchemaTable(schemaTables,name,logger2){let direct=schemaTables[name];if(direct)return direct;let camel=snakeToCamel(name);if(camel!==name){let viaCamel=schemaTables[camel];if(viaCamel)return viaCamel}if(logger2){let keys=Object.keys(schemaTables),preview=keys.slice(0,20).join(", "),suffix=keys.length>20?`, ...(+${keys.length-20} more)`:"";logger2.warn(`[SchemaTables] Table "${name}" not found (tried "${name}" and "${camel}"). Known keys: ${preview}${suffix}`)}return}var init_types4=()=>{};import{and as and4,desc,eq as eq9}from"drizzle-orm";function resolveEnvValue2(value){if(!value)return;return process.env[value]??value}function toCamel(obj){let result={};for(let[key,value]of Object.entries(obj)){let camelKey=key.replace(/_([a-z])/g,(_,c)=>c.toUpperCase());result[camelKey]=value}return result}function fromCamel(obj){let result={};for(let[key,value]of Object.entries(obj)){let snakeKey=key.replace(/[A-Z]/g,(c)=>`_${c.toLowerCase()}`);result[snakeKey]=value}return result}class NotificationService{db;schemaTables;config;logger;getEmailService;constructor(serviceConfig){this.db=serviceConfig.db,this.schemaTables=serviceConfig.schemaTables,this.config=serviceConfig.config,this.logger=serviceConfig.logger,this.getEmailService=serviceConfig.getEmailService}getTable(name){return resolveSchemaTable(this.schemaTables,name,this.logger)}getCol(table,col4){return table[col4]}isChannelEnabled(channel){let channels=this.config.channels;if(!channels)return channel==="portal";switch(channel){case"portal":return channels.portal!==!1;case"email":return channels.email===!0;case"telegram":return channels.telegram?.enabled===!0;case"webhook":return channels.webhook?.enabled===!0;default:return!1}}interpolateTemplate(template,context){let result=template;for(let[key,value]of Object.entries(context))result=result.replace(new RegExp(`{{${key}}}`,"g"),String(value??""));for(let[key,value]of Object.entries(this.config.templateVariables||{}))result=result.replace(new RegExp(`{{${key}}}`,"g"),value);return result}async triggerNotifications(params){let{trigger,flow_id,entity_name,entity_id,node_id,context={}}=params,rulesTable=this.getTable("verificationNotificationRules"),recipientsTable=this.getTable("verificationNotificationRecipients"),channelsTable=this.getTable("verificationNotificationChannels");if(!rulesTable||!recipientsTable){this.logger.warn("[Notification] Notification tables not found");return}let now=new Date,rules=await this.db.select().from(rulesTable).where(and4(eq9(this.getCol(rulesTable,"flowId"),flow_id),eq9(this.getCol(rulesTable,"trigger"),trigger)));this.logger.info(`[Notification] Found ${rules.length} rules for trigger=${trigger} flow_id=${flow_id}, filter_node_id=${node_id||"NONE"}`);for(let r of rules)this.logger.info(`[Notification] Rule ${r.id}: nodeId=${r.nodeId}, trigger=${r.trigger}, title=${JSON.stringify(r.titleTemplate)}`);let filteredRules=rules.filter((rule)=>{if(node_id&&rule.nodeId!==node_id)return this.logger.info(`[Notification] EXCLUDED rule ${rule.id}: rule.nodeId=${rule.nodeId} !== filter_node_id=${node_id}`),!1;if(rule.startsAt&&new Date(rule.startsAt)>now)return!1;if(rule.expiresAt&&new Date(rule.expiresAt)<now)return!1;return!0});for(let rule of filteredRules){let recipients=await this.db.select().from(recipientsTable).where(eq9(this.getCol(recipientsTable,"ruleId"),rule.id)),ruleChannels=["portal"];if(channelsTable){let channelEntries=await this.db.select().from(channelsTable).where(eq9(this.getCol(channelsTable,"ruleId"),rule.id));if(channelEntries.length>0)ruleChannels=channelEntries.map((c)=>c.channel)}let enabledChannels=ruleChannels.filter((ch)=>this.isChannelEnabled(ch));if(enabledChannels.length===0)continue;this.logger.info(`[Notification] Rule ${rule.id}: ${recipients.length} recipients, ${enabledChannels.length} channels (${enabledChannels.join(",")})`);let userIds=await this.resolveRecipients(recipients,params.verifier_id,flow_id,entity_name,entity_id);this.logger.info(`[Notification] Rule ${rule.id}: resolved ${userIds.length} user IDs: ${userIds.join(", ")}`);let enrichedContext={...context,entity_name,entity_id,trigger,decision:params.decision};this.logger.info(`[Notification] Rule ${rule.id}: titleTemplate=${JSON.stringify(rule.titleTemplate)}, bodyTemplate=${JSON.stringify(rule.bodyTemplate)}, context=${JSON.stringify(enrichedContext)}`);let title=rule.titleTemplate?this.interpolateTemplate(rule.titleTemplate,enrichedContext):`Verification ${trigger.replace("on_","").replace("_"," ")}`,body=rule.bodyTemplate?this.interpolateTemplate(rule.bodyTemplate,enrichedContext):void 0;this.logger.info(`[Notification] Rule ${rule.id}: final title="${title}", body="${body}"`);for(let userId of userIds)await this.send({user_id:userId,title,body,entity_name,entity_id,type:"verification",source:`flow:${flow_id}`,channels:enabledChannels})}this.logger.debug(`[Notification] Triggered ${filteredRules.length} rules for ${trigger} on ${entity_name}:${entity_id}`)}async resolveRecipients(recipients,currentVerifierId,flowId,entityName,entityId){let userIds=new Set,userRolesTable=this.getTable("userRoles"),rolesTable=this.getTable("roles");for(let recipient of recipients)switch(recipient.recipientType){case"user":if(recipient.recipientUserId)userIds.add(recipient.recipientUserId);break;case"role":if(recipient.recipientRole&&userRolesTable&&rolesTable){let rolesCols=rolesTable,userRolesCols=userRolesTable,role=(await this.db.select().from(rolesTable).where(eq9(rolesCols.name,recipient.recipientRole)).limit(1))[0];if(role){let usersInRole=await this.db.select({user_id:userRolesCols.userId}).from(userRolesTable).where(eq9(userRolesCols.roleId,role.id));for(let ur of usersInRole)userIds.add(ur.user_id)}}break;case"step_verifier":if(currentVerifierId)userIds.add(currentVerifierId);break;case"entity_creator":{if(entityName&&entityId){let instancesTable=this.getTable("verificationInstances");if(instancesTable){let inst=(await this.db.select().from(instancesTable).where(and4(eq9(this.getCol(instancesTable,"entityName"),entityName),eq9(this.getCol(instancesTable,"entityId"),entityId))).orderBy(desc(this.getCol(instancesTable,"createdAt"))).limit(1))[0];if(inst?.startedBy)userIds.add(inst.startedBy)}}break}case"all_verifiers":{if(flowId){let verifierConfigsTable=this.getTable("verificationVerifierConfigs");if(verifierConfigsTable){let configs=await this.db.select().from(verifierConfigsTable).where(eq9(this.getCol(verifierConfigsTable,"flowId"),flowId));this.logger.info(`[Notification] all_verifiers: found ${configs.length} verifier configs for flow ${flowId}`);for(let cfg of configs){let row=cfg;if(this.logger.info(`[Notification] all_verifiers: config node_id=${row.nodeId}, type=${row.verifierType}, userId=${row.verifierUserId}, role=${row.verifierRole}`),row.verifierUserId)userIds.add(row.verifierUserId);if(row.verifierType==="role"&&row.verifierRole&&userRolesTable&&rolesTable){let rCols=rolesTable,urCols=userRolesTable,roleRow=(await this.db.select().from(rolesTable).where(eq9(rCols.name,row.verifierRole)).limit(1))[0];if(roleRow){let usersInRole=await this.db.select({user_id:urCols.userId}).from(userRolesTable).where(eq9(urCols.roleId,roleRow.id));for(let ur of usersInRole)userIds.add(ur.user_id)}}}}}break}}return Array.from(userIds)}async send(params){let{user_id,title,body,entity_name,entity_id,type,source,channels}=params;for(let channel of channels){if(!this.isChannelEnabled(channel)){this.logger.debug?.(`[Notification] channel "${channel}" is disabled in config \u2014 skipping for user ${user_id}`);continue}switch(channel){case"portal":await this.sendPortalNotification(user_id,title,body,entity_name,entity_id,type,source);break;case"email":await this.sendEmailNotification(user_id,title,body);break;case"sms":this.logger.warn(`[Notification] SMS delivery is not supported \u2014 skipping sms notification for user ${user_id}`);break;case"telegram":await this.sendTelegramNotification(params);break;case"webhook":await this.sendWebhookNotification(params);break}}}async sendPortalNotification(userId,title,body,entityName,entityId,type,source){let notificationsTable=this.getTable("notifications");if(!notificationsTable){this.logger.warn("[Notification] notifications table not found");return}await this.db.insert(notificationsTable).values(toCamel({user_id:userId,title,body:body||null,entity_name:entityName||null,entity_id:entityId||null,type:type||"system",source:source||null,is_seen:!1})),this.logger.debug(`[Notification] Portal notification sent to ${userId}: ${title}`)}async sendTelegramNotification(params){let telegramConfig=this.config.channels?.telegram,botToken=resolveEnvValue2(telegramConfig?.botToken),chatId=resolveEnvValue2(telegramConfig?.chatId);if(!botToken||!chatId){this.logger.warn("[Notification] Telegram channel enabled but botToken/chatId is missing or unresolved \u2014 skipping delivery");return}let text=params.body?`*${params.title}*
@@ -2028,4 +2028,4 @@ ${content}
2028
2028
  ${warningBanner}
2029
2029
  ${deviceTable}
2030
2030
  <p style="margin:16px 0 0;color:#6b7280;font-size:13px;">If this wasn't you, please secure your account immediately.</p>
2031
- `)}deliver(getEmailService(),{to:user.email,subject,html:emailHtml},"[AUTH] login notification").then((report)=>{if(report.level==="warn")logger2.warn(report.message,{userId:params.userId,trustScore,requiresApproval});else logger2.info(report.message,{userId:params.userId})})}}}if(logger2.info("[AUTH] Session saved to DB",{sessionId,userId:params.userId,isNewDevice,requiresApproval,deviceFingerprint,ipAddress:deviceInfo.ipAddress}),deviceTrustMint&&dtTable&&deviceTrustCfg?.enabled)try{let DT2=await Promise.resolve().then(() => (init_DeviceTrust(),exports_DeviceTrust)),DTStore2=await Promise.resolve().then(() => (init_store2(),exports_store)),{randomUUID:randomUUID10}=await import("crypto"),{raw,hash}=DT2.mintDeviceToken(),ttlSec=parseTimeToSeconds2(deviceTrustCfg.tokenExpiresIn??"30d");await DTStore2.insertDeviceRow(db,dtTable,{id:randomUUID10(),userId:params.userId,tokenHash:hash,status:deviceTrustMint,originSessionId:sessionId,label:deviceInfo.deviceName||`${deviceInfo.browserName||"Unknown"} on ${deviceInfo.osName||"Unknown"}`,deviceFingerprint,firstSeenIp:deviceInfo.ipAddress,expiresAt:new Date(Date.now()+ttlSec*1000)});let rawDomainDT=authentication.cookieDomain,resolvedDomainDT=rawDomainDT?process.env[rawDomainDT]??rawDomainDT:void 0,domainDT=resolvedDomainDT==="localhost"||resolvedDomainDT===".localhost"?void 0:resolvedDomainDT;deviceTokenToSet=DT2.buildDeviceTokenCookie(raw,{cookieName:deviceTrustCfg.cookieName,cookiePrefix:deviceTrustCfg.cookiePrefix,sameSite:deviceTrustCfg.sameSite,maxAgeSeconds:ttlSec,domain:domainDT,secure:!!domainDT})}catch(dtErr){logger2.warn("[AUTH] Device-trust token mint failed \u2014 continuing",{userId:params.userId,error:dtErr instanceof Error?dtErr.message:String(dtErr)})}return{requiresApproval,sessionId,deviceTokenCookie:deviceTokenToSet??void 0}},storeResetToken:async(userId,token,expiresAt,reqSchemaName)=>{let resetTokensTable=resolveTableForTenant("passwordResetTokens",reqSchemaName);if(!resetTokensTable||!db)return;await db.insert(resetTokensTable).values({userId,tokenHash:createHash4("sha256").update(token).digest("hex"),expiresAt})},getResetToken:async(token,reqSchemaName)=>{let{eq:eq64}=__require("drizzle-orm"),resetTokensTable=resolveTableForTenant("passwordResetTokens",reqSchemaName);if(!resetTokensTable||!db)return null;let tokenHash=createHash4("sha256").update(token).digest("hex"),row=(await db.select().from(resetTokensTable).where(eq64(resetTokensTable.tokenHash,tokenHash)).limit(1))[0];if(!row||row.usedAt)return null;return{userId:row.userId,expiresAt:row.expiresAt}},deleteResetToken:async(token,reqSchemaName)=>{let{eq:eq64}=__require("drizzle-orm"),resetTokensTable=resolveTableForTenant("passwordResetTokens",reqSchemaName);if(!resetTokensTable||!db)return;let tokenHash=createHash4("sha256").update(token).digest("hex");await db.delete(resetTokensTable).where(eq64(resetTokensTable.tokenHash,tokenHash))},revokeSessionInDb:async(sessionId,reason,reqSchemaName)=>{let revokeTable=resolveTableForTenant("userSessions",reqSchemaName)||resolveTableForTenant("user_sessions",reqSchemaName)||resolveTableForTenant("sessions",reqSchemaName)||sessionsTable;if(!revokeTable||!db)return;await db.update(revokeTable).set({isActive:!1,revokedAt:new Date,revokedReason:reason}).where(eq63(revokeTable.id,sessionId)),logger2.info("[AUTH] Session revoked in DB",{sessionId,reason})},sendResetEmail:async(email,token,request)=>{if(isEmailExempt(email,authentication?.emailExemptDomains))return logger2.info("[AUTH] Skipping reset email \u2014 domain exempt",{email}),{sent:!1,reason:"exempt"};let mailer=getEmailService();if(!mailer?.isAvailable())return logger2.warn("[AUTH] Cannot send reset email \u2014 no email provider configured"),{sent:!1,reason:"no_provider"};let configuredResetUrl=authentication.passwordReset?.redirectUrl||"http://localhost:3000/reset-password",resetUrl=request?buildEmailActionLink({request,configuredUrl:configuredResetUrl,path:extractConfiguredPath(configuredResetUrl,"/reset-password"),query:{token},allowedOrigins:trustedAppOrigins()}):`${configuredResetUrl}?token=${token}`,result=await mailer.sendEmail({to:email,subject:"Password Reset Request",html:`<p>Click the link to reset your password:</p><a href="${resetUrl}">${resetUrl}</a>`});if(result?.success===!1){let refused=result.rejected;return{sent:!1,reason:Array.isArray(refused)&&refused.length>0?"rejected":"error",detail:result.error}}return{sent:!0}},storeMagicToken:async(params,reqSchemaName)=>{let magicTokensTable=resolveTableForTenant("magicLinkTokens",reqSchemaName);if(!magicTokensTable||!db)return;await db.insert(magicTokensTable).values({userId:params.userId,email:params.email,tokenHash:params.tokenHash,expiresAt:params.expiresAt})},getMagicToken:async(tokenHash,reqSchemaName)=>{let{eq:eq64}=__require("drizzle-orm"),magicTokensTable=resolveTableForTenant("magicLinkTokens",reqSchemaName);if(!magicTokensTable||!db)return null;let row=(await db.select().from(magicTokensTable).where(eq64(magicTokensTable.tokenHash,tokenHash)).limit(1))[0];if(!row||row.usedAt)return null;return{userId:row.userId,email:row.email,tokenHash:row.tokenHash,expiresAt:row.expiresAt}},deleteMagicToken:async(tokenHash,reqSchemaName)=>{let{eq:eq64}=__require("drizzle-orm"),magicTokensTable=resolveTableForTenant("magicLinkTokens",reqSchemaName);if(!magicTokensTable||!db)return;await db.delete(magicTokensTable).where(eq64(magicTokensTable.tokenHash,tokenHash))}}}),logger2.info("[AUTH] Routes registered")}}if(resolvedOptions.storage?.enabled&&resolvedOptions.storage?.cdn?.enabled){let{createCdnRoutes:createCdnRoutes2,mergeCdnConfig:mergeCdnConfig2,mergeStorageConfig:mergeStorageConfig2}=(init_storage2(),__toCommonJS(exports_storage)),cdnConfig=mergeCdnConfig2(resolvedOptions.storage.cdn),storageConfig=mergeStorageConfig2(resolvedOptions.storage),filesTable=schemaTables.files;if(plugin.use(createCdnRoutes2({cdn:cdnConfig,storagePath:storageConfig.basePath,logger:logger2,getStorageProvider:()=>storageProvider,buildStorageProvider,getFileRecord:filesTable&&db?async(id,reqSchemaName)=>{let tbl=filesTable;if(reqSchemaName&&tenantRegistry){let ctx=tenantRegistry.getSchemaContext(reqSchemaName);if(ctx?.schemaTables.files)tbl=ctx.schemaTables.files}let t43=tbl,result=await db.select().from(t43).where(eq63(t43.id,id)).limit(1);if(result.length===0)return null;let record3=result[0];return{id:record3.id,name:record3.name,path:record3.path,mime_type:record3.mimeType||record3.mime_type,original_name:record3.originalName||record3.original_name}}:void 0})),logger2.info(`[Storage] CDN routes enabled at ${cdnConfig.basePath}`),filesTable&&db){let{createResumableRoutes:createResumableRoutes2}=(init_resumable(),__toCommonJS(exports_resumable)),resumable=createResumableRoutes2({basePath:storageConfig.basePath,storage:storageConfig,logger:logger2,getUserId:(request)=>request.headers.get("x-user-id"),canUpload:(request)=>{if(resolvedOptions.authorization?.enabled===!1)return!0;return checkAuthorizationFromJWT({userClaims:decodeHeaderList(request.headers.get("x-user-claims")),userRoles:decodeHeaderList(request.headers.get("x-user-roles")),claimScopes:decodeClaimScopesHeader(request.headers.get("x-user-claim-scopes")),method:"POST",entity:"files",logger:logger2,requireClaimSpecificity:resolvedOptions.authorization?.requireClaimSpecificity}).authorized},register:async(input)=>{let nodePath=__require("path"),{rename:rename6}=__require("fs/promises"),{randomUUID:randomUUID10}=__require("crypto"),id=randomUUID10(),extension2=nodePath.extname(input.originalName),storedName=`${id}${extension2}`,destination=nodePath.join(storageConfig.basePath,storedName);try{await rename6(input.partPath,destination)}catch(error3){return logger2.error("[Storage] A finished upload could not be moved into place",{error:error3}),null}let uploaded={id,name:storedName,originalName:input.originalName,path:storageConfig.basePath,mimeType:input.mimeType,size:input.size,createdAt:new Date},{buildFileRecordPayload:buildFileRecordPayload2,scheduleUploadMediaProcessing:scheduleUploadMediaProcessing2}=(init_storage2(),__toCommonJS(exports_storage)),t43=filesTable,payload={...buildFileRecordPayload2(uploaded,input.userId),type:input.type},row=(await db.insert(t43).values(payload).returning())[0];if(!row?.id)return null;return scheduleUploadMediaProcessing2([uploaded],{storagePath:storageConfig.basePath,media:{transform:mergeTransformConfig(resolvedOptions.storage?.cdn?.transform),video:mergeVideoConfig(resolvedOptions.storage?.cdn?.video),pdf:mergePdfConfig(resolvedOptions.storage?.cdn?.pdf)},logger:logger2}),{id:String(row.id)}}});plugin.use(resumable.plugin),setInterval(()=>{resumable.sweep(Date.now()).then((removed)=>{if(removed>0)logger2.info(`[Storage] Reclaimed ${removed} abandoned upload(s)`)}).catch(()=>{})},3600000).unref?.(),logger2.info("[Storage] Resumable uploads enabled at /uploads")}}if(resolvedOptions.notification?.enabled&&db){let notificationConfig=resolvedOptions.notification;if(notificationConfig.endpoints?.enabled!==!1){warnOnEntityBasePathCollision("Notification",notificationConfig.endpoints?.basePath||"/notifications",resolvedOptions.entities,logger2);let{routes:notificationRoutes}=createNotificationRoutes({db,schemaTables,config:notificationConfig,logger:logger2,getEmailService,tenantRegistry});plugin.use(notificationRoutes),logger2.info(`[Notification] Routes registered at ${notificationConfig.endpoints?.basePath||"/notifications"}`)}}if(resolvedOptions.verification?.enabled&&db){let{routes:verificationRoutes}=createVerificationRoutes({db,schemaTables,config:resolvedOptions.verification,notificationConfig:resolvedOptions.notification,logger:logger2,getEmailService,tenantRegistry});plugin.use(verificationRoutes)}if(resolvedOptions.backup?.enabled&&db){let{createBackupRoutes:createBackupRoutes2}=(init_backup(),__toCommonJS(exports_backup)),{routes:backupRoutes}=createBackupRoutes2({db,logger:logger2,config:{enabled:!0,basePath:resolvedOptions.backup.basePath||"/admin/backup",storagePath:resolvedOptions.backup.storagePath||"./backups",format:resolvedOptions.backup.format||"json",maxBackups:resolvedOptions.backup.maxBackups||50,allowRestore:resolvedOptions.backup.allowRestore??!0,excludeTables:resolvedOptions.backup.excludeTables||["audit_logs","backup_logs"],encryptionKey:resolvedOptions.backup.encryptionKey?process.env[resolvedOptions.backup.encryptionKey]||resolvedOptions.backup.encryptionKey:void 0,schedule:{enabled:resolvedOptions.backup.schedule?.enabled??!1,cron:resolvedOptions.backup.schedule?.cron||"0 2 * * *",retentionDays:resolvedOptions.backup.schedule?.retentionDays||30,timeZone:resolvedOptions.backup.schedule?.timeZone}},schemaTables,schemaName:targetSchemaName,tenantRegistry,lock:getRedisManager()});plugin.use(backupRoutes),logger2.info("[Backup] Routes registered",{basePath:resolvedOptions.backup.basePath||"/admin/backup",scheduleEnabled:resolvedOptions.backup.schedule?.enabled??!1})}if(resolvedOptions.authorization?.endpointDiscovery?.enabled&&db&&resolvedOptions.authentication?.mode!=="consumer"){let{createAuthorizationDiscoveryRoutes:createAuthorizationDiscoveryRoutes2}=(init_authorization(),__toCommonJS(exports_authorization));plugin.use(createAuthorizationDiscoveryRoutes2({db,schemaTables,logger:logger2,discovery:resolvedOptions.authorization.endpointDiscovery,readState:async(key2)=>{let rm3=getRedisManager();if(!rm3)return null;let res=await rm3.read(key2);return res?.success?res.data:null}})),logger2.info("[Authorization] Endpoint-discovery routes registered",{discover:"/authorization/discover",manifest:"/authorization/route-manifest"})}let pubsubClientManager=null;if(resolvedOptions.pubsub?.enabled){let redis=getRedisManager();if(redis){let pubsubConfig=resolvedOptions.pubsub,{plugin:pubsubPlugin,clientManager}=createPubSubRoutes({redis,logger:logger2,basePath:pubsubConfig.basePath||"/subs",wsPath:pubsubConfig.wsPath||"/api/events/subscribe",pubsubName:pubsubConfig.pubsubName||"pubsub-redis",maxClientsPerUser:pubsubConfig.maxClientsPerUser??10,maxTopicsPerClient:pubsubConfig.maxTopicsPerClient??64,wsIdleTimeout:pubsubConfig.wsIdleTimeout??120,ack:{enabled:pubsubConfig.ack?.enabled??!0,ttlSeconds:pubsubConfig.ack?.ttlSeconds??300,maxRetries:pubsubConfig.ack?.maxRetries??3,retryIntervalMs:pubsubConfig.ack?.retryIntervalMs??5000},presence:{enabled:pubsubConfig.presence?.enabled??!0,debounceMs:pubsubConfig.presence?.debounceMs??5000},cleanupIntervalMs:pubsubConfig.cleanupIntervalMs??60000,daprApiToken:pubsubConfig.daprApiToken,getLiveMonitoringService:()=>liveMonitoringService,authenticate:envResolved.accessTokenSecret?(headers)=>{let wsTokens=parseTokenValuesFromHeaders(headers,tokenNames);if(!wsTokens.access_token)return logger2.debug("[PubSub] WS handshake rejected: no access token presented"),null;let jwtResult=verifyJWT(wsTokens.access_token,envResolved.accessTokenSecret||"",{issuer:authentication?.accessToken?.issuer,audience:authentication?.accessToken?.audience});if(!jwtResult.valid)return logger2.warn("[PubSub] WS handshake rejected: JWT verification failed",{reason:jwtResult.error}),null;let sub=jwtResult.payload?.sub;return typeof sub==="string"&&sub.length>0?{userId:sub}:null}:void 0});plugin.use(pubsubPlugin),pubsubClientManager=clientManager,logger2.info("[PubSub] Enabled",{basePath:pubsubConfig.basePath||"/subs",wsPath:pubsubConfig.wsPath||"/api/events/subscribe"})}else logger2.warn("[PubSub] pubsub is enabled but Redis is not configured. Disabling PubSub.")}if(resolvedOptions.chat?.enabled&&db){let chatConfig=mergeChatConfig(resolvedOptions.chat),chatStorageConfig=mergeStorageConfig(resolvedOptions.storage),attachmentsAvailable=resolvedOptions.storage?.enabled===!0&&resolvedOptions.storage?.cdn?.enabled===!0&&chatConfig.attachments.enabled,cdnBasePath=resolvedOptions.storage?.cdn?.basePath||"/cdn",cdnMedia={transform:mergeTransformConfig(resolvedOptions.storage?.cdn?.transform),video:mergeVideoConfig(resolvedOptions.storage?.cdn?.video),pdf:mergePdfConfig(resolvedOptions.storage?.cdn?.pdf)},{routes:chatRoutes}=createChatRoutes({db,schemaTables,config:chatConfig,logger:logger2,broadcaster:pubsubClientManager,tenantRegistry,storageConfig:chatStorageConfig,attachmentsAvailable,cdnBasePath,cdnMedia});plugin.use(chatRoutes),logger2.info("[Chat] Enabled",{basePath:chatConfig.basePath,attachmentsAvailable,realtime:pubsubClientManager!==null})}if(resolvedOptions.payment?.enabled&&db){let{createPaymentService:createPaymentService2}=(init_Payment(),__toCommonJS(exports_Payment)),{createPaymentRoutes:createPaymentRoutes2}=(init_payment(),__toCommonJS(exports_payment)),paymentService=createPaymentService2(resolvedOptions);if(paymentService){let paymentConfig=resolvedOptions.payment,marketplaceServices=null;if(paymentConfig?.marketplace?.enabled){let{createMarketplaceServices:createMarketplaceServices2}=(init_Payment(),__toCommonJS(exports_Payment));marketplaceServices=createMarketplaceServices2({options:resolvedOptions,logger:logger2,getDb:()=>db,provider:paymentService.providerInstance,schemaTables})}let paymentRoutes=createPaymentRoutes2({provider:paymentService.providerInstance,webhookSecret:paymentService.webhookSecret,getPayoutService:()=>marketplaceServices?.payoutService??null,basePath:paymentConfig?.basePath||"/payment",defaultCurrency:paymentConfig?.defaultCurrency||"TRY",defaultLocale:paymentConfig?.defaultLocale||"tr",successRedirectUrl:paymentConfig?.successRedirectUrl||"/payment/success",failedRedirectUrl:paymentConfig?.failedRedirectUrl||"/payment/failed",errorRedirectUrl:paymentConfig?.errorRedirectUrl||"/payment/error",callbackUrl:paymentConfig?.callbackUrl,savedMethodsEnabled:paymentConfig?.savedMethodsEnabled??!0,threeDSecureEnabled:paymentConfig?.threeDSecureEnabled??!0,subMerchantsEnabled:paymentConfig?.subMerchantsEnabled??!1,transactionsTable:schemaTables.paymentTransactions??schemaTables.payment_transactions,methodsTable:schemaTables.paymentMethods??schemaTables.payment_methods,webhookLogsTable:schemaTables.paymentWebhookLogs??schemaTables.payment_webhook_logs,subMerchantsTable:schemaTables.paymentSubMerchants??schemaTables.payment_sub_merchants,commissionSplitsTable:schemaTables.paymentCommissionSplits??schemaTables.payment_commission_splits,productsTable:schemaTables.paymentProducts??schemaTables.payment_products,pricesTable:schemaTables.paymentPrices??schemaTables.payment_prices,customersTable:schemaTables.paymentCustomers??schemaTables.payment_customers,subscriptionsTable:schemaTables.paymentSubscriptions??schemaTables.payment_subscriptions,invoicesTable:schemaTables.paymentInvoices??schemaTables.payment_invoices,db,logger:logger2});if(plugin.use(paymentRoutes),logger2.info("[Payment] Routes registered",{basePath:paymentConfig?.basePath||"/payment",provider:paymentService.provider}),marketplaceServices){let{createMarketplaceRoutes:createMarketplaceRoutes2}=(init_marketplace(),__toCommonJS(exports_marketplace)),services=marketplaceServices;createMarketplaceRoutes2(plugin,{getMarketplaceService:()=>services.marketplaceService,getPayoutService:()=>services.payoutService,basePath:paymentConfig?.basePath||"/payment",logger:logger2}),logger2.info("[Payment] Marketplace routes registered",{basePath:`${paymentConfig?.basePath||"/payment"}/marketplace`})}}}let cohortConfig=authentication?.cohorts;if(cohortConfig?.enabled!==!1&&!isConsumerMode&&db){let cohortBasePath=cohortConfig?.basePath||"/auth/admin/cohorts",resolveTable=(key2,fallback)=>schemaTables[key2]??(fallback?schemaTables[fallback]:null)??null,resolvedCohortsTable=resolveTable("userCohorts","user_cohorts");if(!resolvedCohortsTable)logger2.warn("[Cohort] user_cohorts table not found in schema \u2014 cohort routes will be inactive. Re-generate schema with nucleus-core >= 0.9.120");if(plugin.use(createCohortRoutes({db,logger:logger2,cohortsTable:resolvedCohortsTable,usersTable:resolveTable("users"),rolesTable:resolveTable("roles"),userRolesTable:resolveTable("userRoles","user_roles"),profilesTable:resolveTable("profiles"),basePath:cohortBasePath,get defaultRole(){return resolvedOptions.authentication?.defaultRole},get defaultRoles(){return resolvedOptions.authentication?.defaultRoles},emailExemptDomains:authentication?.emailExemptDomains,passwordPolicy:authentication?.passwordPolicy})),resolvedCohortsTable)logger2.info("[Cohort] Routes registered",{basePath:cohortBasePath})}if(resolvedOptions.configManagement?.enabled){let configBasePath=resolvedOptions.configManagement.basePath||"/nucleus/config";plugin.use(createConfigRoutes({logger:logger2,resolvedOptions,configFilePath,basePath:configBasePath,db,schemaTables,getRedis:()=>{let redis=getRedisManager();if(!redis)return null;return{read:async(key2)=>{let r2=await redis.read(key2);return{success:r2.success,data:r2.success?r2.data:null}},create:async(key2,value2)=>{return{success:(await redis.create(key2,value2)).success}}}},onConfigUpdate:(section,_newValue)=>{logger2.info(`[ConfigManagement] Section "${section}" updated in-memory`,{section})}})),logger2.info(`[ConfigManagement] Routes enabled at ${configBasePath}`)}if(resolvedOptions.secrets?.enabled){let secretsBasePath=resolvedOptions.secrets.basePath||"/secrets";warnOnEntityBasePathCollision("Secrets",secretsBasePath,resolvedOptions.entities,logger2),plugin.use(createSecretsRoutes({logger:logger2,basePath:secretsBasePath,db,schemaTables,resolvedOptions,getService:()=>secretsService,getEmailService,getStorageProvider:()=>storageProvider,buildStorageProvider,adminRoles:resolvedOptions.secrets.adminRoles})),logger2.info(`[Secrets] Routes enabled at ${secretsBasePath}`)}if(resolvedOptions.integrations?.enabled){let integrationsBasePath=resolvedOptions.integrations.basePath||"/integrations";warnOnEntityBasePathCollision("Integrations",integrationsBasePath,resolvedOptions.entities,logger2),plugin.use(createIntegrationRoutes({logger:logger2,basePath:integrationsBasePath,db,schemaTables,resolvedOptions,getEncryptionKey:()=>resolveMasterKey(resolvedOptions.secrets?.encryptionKey)??null,getLockStore:()=>getRedisManager(),publish:(topic,payload)=>pubsubClientManager?.broadcastEvent(topic,payload),adminRoles:resolvedOptions.integrations.adminRoles}))}plugin.onStart((app)=>{let requiredBody=requiredBodyCeiling({storageEnabled:resolvedOptions.storage?.enabled,maxFileSizeBytes:resolvedOptions.storage?.maxFileSizeBytes,maxFiles:resolvedOptions.storage?.formData?.maxFiles}),complaint=bodyCeilingComplaint({configured:app.config?.serve?.maxRequestBodySize,required:requiredBody});if(complaint)logger2.error(`[Storage] ${complaint}`);let memoryComplaint=memoryCeilingComplaint({required:requiredBody,cgroupLimit:readCgroupLimitBytes()});if(memoryComplaint)logger2.error(`[Storage] ${memoryComplaint}`);let memoryAdvice=readRuntimeMemoryAdvice();if(memoryAdvice){let mib=(bytes)=>`${Math.round(bytes/1024/1024)} MiB`;logger2.warn(`[Runtime] The JS engine sizes its heap for ${mib(memoryAdvice.hostTotal)} but this container is capped at ${mib(memoryAdvice.cgroupLimit)} \u2014 it will grow past the `+`limit and be OOM-killed. Set BUN_JSC_forceRAMSize=${memoryAdvice.recommended} (${mib(memoryAdvice.recommended)}) in the deployment env.`)}let port=Number(process.env.PORT)||3000,appId=resolvedOptions.appId||"nucleus",mode=resolvedOptions.mode||"production";console.log(""),console.log(` \x1B[32m\uD83D\uDE80 ${appId}\x1B[0m \x1B[90mv${Date.now()}\x1B[0m`),console.log(` \x1B[36m\u279C\x1B[0m Local: \x1B[36mhttp://localhost:${port}\x1B[0m`),console.log(` \x1B[36m\u279C\x1B[0m Mode: \x1B[33m${mode}\x1B[0m`),console.log("")});let shuttingDown=!1,drainResources=async(trigger)=>{if(shuttingDown)return;shuttingDown=!0,logger2.info(`[Shutdown] ${trigger} \u2014 draining resources`);try{liveMonitoringService?.stop(),monitoringService?.stop(),secretsService?.stop(),storageProvider?.close()}catch(err){logger2.warn("[Shutdown] failed stopping monitoring",{error:err instanceof Error?err.message:String(err)})}try{let redisClient=getRedisManager()?.getDirectClient?.();if(redisClient)await redisClient.quit()}catch(err){logger2.warn("[Shutdown] failed closing Redis",{error:err instanceof Error?err.message:String(err)})}try{for(let timer of backgroundIntervals)clearInterval(timer);backgroundIntervals.length=0}catch(err){logger2.warn("[Shutdown] failed clearing background intervals",{error:err instanceof Error?err.message:String(err)})}try{let{destroyAckCleanup:destroyAckCleanup2}=await Promise.resolve().then(() => (init_ack_manager(),exports_ack_manager));destroyAckCleanup2()}catch{}try{if(dbPool)await dbPool.end()}catch(err){logger2.warn("[Shutdown] failed draining DB pool",{error:err instanceof Error?err.message:String(err)})}logger2.info("[Shutdown] complete")};if(plugin.onStop(async()=>{await drainResources("Elysia onStop")}),resolvedOptions.gracefulShutdown!==!1){let onSignal=(signal)=>{drainResources(signal).finally(()=>process.exit(0))};process.once("SIGTERM",()=>onSignal("SIGTERM")),process.once("SIGINT",()=>onSignal("SIGINT"))}return plugin}init_AzureEmailService();init_Gmail();export{usePubSubStore,usePubSub,serverFetch,requiredBodyCeiling,parseCron2 as parseCron,matchesCron,isDue,generateVerificationEndpoints,generateTenantEndpoints,generateSystemTableEndpoints,generateMonitoringEndpoints,generateMarketplaceEndpoints,generateEndpointsFromConfig,generateDomainEndpoints,generateCohortEndpoints,generateChatEndpoints,generateAuthEndpoints,generateAllEndpoints,generateAdminEndpoints,describeCron,decodeHeaderList,createServerFactory,createApiHook,VERIFICATION_ENDPOINTS,TENANT_ENDPOINTS,ServerFetch,SYSTEM_TABLES,PAYMENT_ENDPOINTS,NucleusElysiaPlugin,MONITORING_ENDPOINTS,MARKETPLACE_ENDPOINTS,GmailService,DOMAIN_ENDPOINTS,CONFIG_ENDPOINTS,COHORT_ENDPOINTS,CHAT_ENDPOINTS,AzureEmailService,AUTH_ENDPOINT_CONFIGS,AUTH_ENDPOINTS};
2031
+ `)}deliver(getEmailService(),{to:user.email,subject,html:emailHtml},"[AUTH] login notification").then((report)=>{if(report.level==="warn")logger2.warn(report.message,{userId:params.userId,trustScore,requiresApproval});else logger2.info(report.message,{userId:params.userId})})}}}if(logger2.info("[AUTH] Session saved to DB",{sessionId,userId:params.userId,isNewDevice,requiresApproval,deviceFingerprint,ipAddress:deviceInfo.ipAddress}),deviceTrustMint&&dtTable&&deviceTrustCfg?.enabled)try{let DT2=await Promise.resolve().then(() => (init_DeviceTrust(),exports_DeviceTrust)),DTStore2=await Promise.resolve().then(() => (init_store2(),exports_store)),{randomUUID:randomUUID10}=await import("crypto"),{raw,hash}=DT2.mintDeviceToken(),ttlSec=parseTimeToSeconds2(deviceTrustCfg.tokenExpiresIn??"30d");await DTStore2.insertDeviceRow(db,dtTable,{id:randomUUID10(),userId:params.userId,tokenHash:hash,status:deviceTrustMint,originSessionId:sessionId,label:deviceInfo.deviceName||`${deviceInfo.browserName||"Unknown"} on ${deviceInfo.osName||"Unknown"}`,deviceFingerprint,firstSeenIp:deviceInfo.ipAddress,expiresAt:new Date(Date.now()+ttlSec*1000)});let rawDomainDT=authentication.cookieDomain,resolvedDomainDT=rawDomainDT?process.env[rawDomainDT]??rawDomainDT:void 0,domainDT=resolvedDomainDT==="localhost"||resolvedDomainDT===".localhost"?void 0:resolvedDomainDT;deviceTokenToSet=DT2.buildDeviceTokenCookie(raw,{cookieName:deviceTrustCfg.cookieName,cookiePrefix:deviceTrustCfg.cookiePrefix,sameSite:deviceTrustCfg.sameSite,maxAgeSeconds:ttlSec,domain:domainDT,secure:!!domainDT})}catch(dtErr){logger2.warn("[AUTH] Device-trust token mint failed \u2014 continuing",{userId:params.userId,error:dtErr instanceof Error?dtErr.message:String(dtErr)})}return{requiresApproval,sessionId,deviceTokenCookie:deviceTokenToSet??void 0}},storeResetToken:async(userId,token,expiresAt,reqSchemaName)=>{let resetTokensTable=resolveTableForTenant("passwordResetTokens",reqSchemaName);if(!resetTokensTable||!db)return;await db.insert(resetTokensTable).values({userId,tokenHash:createHash4("sha256").update(token).digest("hex"),expiresAt})},getResetToken:async(token,reqSchemaName)=>{let{eq:eq64}=__require("drizzle-orm"),resetTokensTable=resolveTableForTenant("passwordResetTokens",reqSchemaName);if(!resetTokensTable||!db)return null;let tokenHash=createHash4("sha256").update(token).digest("hex"),row=(await db.select().from(resetTokensTable).where(eq64(resetTokensTable.tokenHash,tokenHash)).limit(1))[0];if(!row||row.usedAt)return null;return{userId:row.userId,expiresAt:row.expiresAt}},deleteResetToken:async(token,reqSchemaName)=>{let{eq:eq64}=__require("drizzle-orm"),resetTokensTable=resolveTableForTenant("passwordResetTokens",reqSchemaName);if(!resetTokensTable||!db)return;let tokenHash=createHash4("sha256").update(token).digest("hex");await db.delete(resetTokensTable).where(eq64(resetTokensTable.tokenHash,tokenHash))},revokeSessionInDb:async(sessionId,reason,reqSchemaName)=>{let revokeTable=resolveTableForTenant("userSessions",reqSchemaName)||resolveTableForTenant("user_sessions",reqSchemaName)||resolveTableForTenant("sessions",reqSchemaName)||sessionsTable;if(!revokeTable||!db)return;await db.update(revokeTable).set({isActive:!1,revokedAt:new Date,revokedReason:reason}).where(eq63(revokeTable.id,sessionId)),logger2.info("[AUTH] Session revoked in DB",{sessionId,reason})},sendResetEmail:async(email,token,request)=>{if(isEmailExempt(email,authentication?.emailExemptDomains))return logger2.info("[AUTH] Skipping reset email \u2014 domain exempt",{email}),{sent:!1,reason:"exempt"};let mailer=getEmailService();if(!mailer?.isAvailable())return logger2.warn("[AUTH] Cannot send reset email \u2014 no email provider configured"),{sent:!1,reason:"no_provider"};let configuredResetUrl=authentication.passwordReset?.redirectUrl||"http://localhost:3000/reset-password",resetUrl=request?buildEmailActionLink({request,configuredUrl:configuredResetUrl,path:extractConfiguredPath(configuredResetUrl,"/reset-password"),query:{token},allowedOrigins:trustedAppOrigins()}):`${configuredResetUrl}?token=${token}`,result=await mailer.sendEmail({to:email,subject:"Password Reset Request",html:`<p>Click the link to reset your password:</p><a href="${resetUrl}">${resetUrl}</a>`});if(result?.success===!1){let refused=result.rejected;return{sent:!1,reason:Array.isArray(refused)&&refused.length>0?"rejected":"error",detail:result.error}}return{sent:!0}},storeMagicToken:async(params,reqSchemaName)=>{let magicTokensTable=resolveTableForTenant("magicLinkTokens",reqSchemaName);if(!magicTokensTable||!db)return;await db.insert(magicTokensTable).values({userId:params.userId,email:params.email,tokenHash:params.tokenHash,expiresAt:params.expiresAt})},getMagicToken:async(tokenHash,reqSchemaName)=>{let{eq:eq64}=__require("drizzle-orm"),magicTokensTable=resolveTableForTenant("magicLinkTokens",reqSchemaName);if(!magicTokensTable||!db)return null;let row=(await db.select().from(magicTokensTable).where(eq64(magicTokensTable.tokenHash,tokenHash)).limit(1))[0];if(!row||row.usedAt)return null;return{userId:row.userId,email:row.email,tokenHash:row.tokenHash,expiresAt:row.expiresAt}},deleteMagicToken:async(tokenHash,reqSchemaName)=>{let{eq:eq64}=__require("drizzle-orm"),magicTokensTable=resolveTableForTenant("magicLinkTokens",reqSchemaName);if(!magicTokensTable||!db)return;await db.delete(magicTokensTable).where(eq64(magicTokensTable.tokenHash,tokenHash))}}}),logger2.info("[AUTH] Routes registered")}}if(resolvedOptions.storage?.enabled&&resolvedOptions.storage?.cdn?.enabled){let{createCdnRoutes:createCdnRoutes2,mergeCdnConfig:mergeCdnConfig2,mergeStorageConfig:mergeStorageConfig2}=(init_storage2(),__toCommonJS(exports_storage)),cdnConfig=mergeCdnConfig2(resolvedOptions.storage.cdn),storageConfig=mergeStorageConfig2(resolvedOptions.storage),filesTable=schemaTables.files;if(plugin.use(createCdnRoutes2({cdn:cdnConfig,storagePath:storageConfig.basePath,logger:logger2,getStorageProvider:()=>storageProvider,buildStorageProvider,getFileRecord:filesTable&&db?async(id,reqSchemaName)=>{let tbl=filesTable;if(reqSchemaName&&tenantRegistry){let ctx=tenantRegistry.getSchemaContext(reqSchemaName);if(ctx?.schemaTables.files)tbl=ctx.schemaTables.files}let t43=tbl,result=await db.select().from(t43).where(eq63(t43.id,id)).limit(1);if(result.length===0)return null;let record3=result[0];return{id:record3.id,name:record3.name,path:record3.path,mime_type:record3.mimeType||record3.mime_type,original_name:record3.originalName||record3.original_name}}:void 0})),logger2.info(`[Storage] CDN routes enabled at ${cdnConfig.basePath}`),filesTable&&db){let{createResumableRoutes:createResumableRoutes2}=(init_resumable(),__toCommonJS(exports_resumable)),resumable=createResumableRoutes2({basePath:storageConfig.basePath,storage:storageConfig,logger:logger2,getUserId:(request)=>request.headers.get("x-user-id"),canUpload:(request)=>{if(resolvedOptions.authorization?.enabled===!1)return!0;return checkAuthorizationFromJWT({userClaims:decodeHeaderList(request.headers.get("x-user-claims")),userRoles:decodeHeaderList(request.headers.get("x-user-roles")),claimScopes:decodeClaimScopesHeader(request.headers.get("x-user-claim-scopes")),method:"POST",entity:"files",logger:logger2,requireClaimSpecificity:resolvedOptions.authorization?.requireClaimSpecificity}).authorized},register:async(input)=>{let nodePath=__require("path"),{rename:rename6}=__require("fs/promises"),{randomUUID:randomUUID10}=__require("crypto"),id=randomUUID10(),extension2=nodePath.extname(input.originalName),storedName=`${id}${extension2}`,destination=nodePath.join(storageConfig.basePath,storedName);try{await rename6(input.partPath,destination)}catch(error3){return logger2.error("[Storage] A finished upload could not be moved into place",{error:error3}),null}let uploaded={id,name:storedName,originalName:input.originalName,path:storageConfig.basePath,mimeType:input.mimeType,size:input.size,createdAt:new Date},{buildFileRecordPayload:buildFileRecordPayload2,scheduleUploadMediaProcessing:scheduleUploadMediaProcessing2}=(init_storage2(),__toCommonJS(exports_storage)),t43=filesTable,payload={...buildFileRecordPayload2(uploaded,input.userId),type:input.type},row=(await db.insert(t43).values(payload).returning())[0];if(!row?.id)return null;return scheduleUploadMediaProcessing2([uploaded],{storagePath:storageConfig.basePath,media:{transform:mergeTransformConfig(resolvedOptions.storage?.cdn?.transform),video:mergeVideoConfig(resolvedOptions.storage?.cdn?.video),pdf:mergePdfConfig(resolvedOptions.storage?.cdn?.pdf)},logger:logger2}),{id:String(row.id)}}});plugin.use(resumable.plugin),setInterval(()=>{resumable.sweep(Date.now()).then((removed)=>{if(removed>0)logger2.info(`[Storage] Reclaimed ${removed} abandoned upload(s)`)}).catch(()=>{})},3600000).unref?.(),logger2.info("[Storage] Resumable uploads enabled at /uploads")}}if(resolvedOptions.notification?.enabled&&db){let notificationConfig=resolvedOptions.notification;if(notificationConfig.endpoints?.enabled!==!1){warnOnEntityBasePathCollision("Notification",notificationConfig.endpoints?.basePath||"/notifications",resolvedOptions.entities,logger2);let{routes:notificationRoutes}=createNotificationRoutes({db,schemaTables,config:notificationConfig,logger:logger2,getEmailService,tenantRegistry});plugin.use(notificationRoutes),logger2.info(`[Notification] Routes registered at ${notificationConfig.endpoints?.basePath||"/notifications"}`)}}if(resolvedOptions.verification?.enabled&&db){let{routes:verificationRoutes}=createVerificationRoutes({db,schemaTables,config:resolvedOptions.verification,notificationConfig:resolvedOptions.notification,logger:logger2,getEmailService,tenantRegistry});plugin.use(verificationRoutes)}if(resolvedOptions.backup?.enabled&&db){let{createBackupRoutes:createBackupRoutes2}=(init_backup(),__toCommonJS(exports_backup)),{routes:backupRoutes}=createBackupRoutes2({db,logger:logger2,config:{enabled:!0,basePath:resolvedOptions.backup.basePath||"/admin/backup",storagePath:resolvedOptions.backup.storagePath||"./backups",format:resolvedOptions.backup.format||"json",maxBackups:resolvedOptions.backup.maxBackups||50,allowRestore:resolvedOptions.backup.allowRestore??!0,excludeTables:resolvedOptions.backup.excludeTables||["audit_logs","backup_logs"],encryptionKey:resolvedOptions.backup.encryptionKey?process.env[resolvedOptions.backup.encryptionKey]||resolvedOptions.backup.encryptionKey:void 0,schedule:{enabled:resolvedOptions.backup.schedule?.enabled??!1,cron:resolvedOptions.backup.schedule?.cron||"0 2 * * *",retentionDays:resolvedOptions.backup.schedule?.retentionDays||30,timeZone:resolvedOptions.backup.schedule?.timeZone}},schemaTables,schemaName:targetSchemaName,tenantRegistry,lock:getRedisManager()});plugin.use(backupRoutes),logger2.info("[Backup] Routes registered",{basePath:resolvedOptions.backup.basePath||"/admin/backup",scheduleEnabled:resolvedOptions.backup.schedule?.enabled??!1})}if(resolvedOptions.authorization?.endpointDiscovery?.enabled&&db&&resolvedOptions.authentication?.mode!=="consumer"){let{createAuthorizationDiscoveryRoutes:createAuthorizationDiscoveryRoutes2}=(init_authorization(),__toCommonJS(exports_authorization));plugin.use(createAuthorizationDiscoveryRoutes2({db,schemaTables,logger:logger2,discovery:resolvedOptions.authorization.endpointDiscovery,readState:async(key2)=>{let rm3=getRedisManager();if(!rm3)return null;let res=await rm3.read(key2);return res?.success?res.data:null}})),logger2.info("[Authorization] Endpoint-discovery routes registered",{discover:"/authorization/discover",manifest:"/authorization/route-manifest"})}let pubsubClientManager=null;if(resolvedOptions.pubsub?.enabled){let redis=getRedisManager();if(redis){let pubsubConfig=resolvedOptions.pubsub,{plugin:pubsubPlugin,clientManager}=createPubSubRoutes({redis,logger:logger2,basePath:pubsubConfig.basePath||"/subs",wsPath:pubsubConfig.wsPath||"/api/events/subscribe",pubsubName:pubsubConfig.pubsubName||"pubsub-redis",maxClientsPerUser:pubsubConfig.maxClientsPerUser??10,maxTopicsPerClient:pubsubConfig.maxTopicsPerClient??64,wsIdleTimeout:pubsubConfig.wsIdleTimeout??120,ack:{enabled:pubsubConfig.ack?.enabled??!0,ttlSeconds:pubsubConfig.ack?.ttlSeconds??300,maxRetries:pubsubConfig.ack?.maxRetries??3,retryIntervalMs:pubsubConfig.ack?.retryIntervalMs??5000},presence:{enabled:pubsubConfig.presence?.enabled??!0,debounceMs:pubsubConfig.presence?.debounceMs??5000},cleanupIntervalMs:pubsubConfig.cleanupIntervalMs??60000,daprApiToken:pubsubConfig.daprApiToken,getLiveMonitoringService:()=>liveMonitoringService,authenticate:envResolved.accessTokenSecret?(headers)=>{let wsTokens=parseTokenValuesFromHeaders(headers,tokenNames);if(!wsTokens.access_token)return logger2.debug("[PubSub] WS handshake rejected: no access token presented"),null;let jwtResult=verifyJWT(wsTokens.access_token,envResolved.accessTokenSecret||"",{issuer:authentication?.accessToken?.issuer,audience:authentication?.accessToken?.audience});if(!jwtResult.valid)return logger2.warn("[PubSub] WS handshake rejected: JWT verification failed",{reason:jwtResult.error}),null;let sub=jwtResult.payload?.sub;return typeof sub==="string"&&sub.length>0?{userId:sub}:null}:void 0});plugin.use(pubsubPlugin),pubsubClientManager=clientManager,logger2.info("[PubSub] Enabled",{basePath:pubsubConfig.basePath||"/subs",wsPath:pubsubConfig.wsPath||"/api/events/subscribe"})}else logger2.warn("[PubSub] pubsub is enabled but Redis is not configured. Disabling PubSub.")}if(resolvedOptions.chat?.enabled&&db){let chatConfig=mergeChatConfig(resolvedOptions.chat),chatStorageConfig=mergeStorageConfig(resolvedOptions.storage),attachmentsAvailable=resolvedOptions.storage?.enabled===!0&&resolvedOptions.storage?.cdn?.enabled===!0&&chatConfig.attachments.enabled,cdnBasePath=resolvedOptions.storage?.cdn?.basePath||"/cdn",cdnMedia={transform:mergeTransformConfig(resolvedOptions.storage?.cdn?.transform),video:mergeVideoConfig(resolvedOptions.storage?.cdn?.video),pdf:mergePdfConfig(resolvedOptions.storage?.cdn?.pdf)},{routes:chatRoutes}=createChatRoutes({db,schemaTables,config:chatConfig,logger:logger2,broadcaster:pubsubClientManager,tenantRegistry,storageConfig:chatStorageConfig,attachmentsAvailable,cdnBasePath,cdnMedia});plugin.use(chatRoutes),logger2.info("[Chat] Enabled",{basePath:chatConfig.basePath,attachmentsAvailable,realtime:pubsubClientManager!==null})}if(resolvedOptions.payment?.enabled&&db){let{createPaymentService:createPaymentService2}=(init_Payment(),__toCommonJS(exports_Payment)),{createPaymentRoutes:createPaymentRoutes2}=(init_payment(),__toCommonJS(exports_payment)),paymentService=createPaymentService2(resolvedOptions);if(paymentService){let paymentConfig=resolvedOptions.payment,marketplaceServices=null;if(paymentConfig?.marketplace?.enabled){let{createMarketplaceServices:createMarketplaceServices2}=(init_Payment(),__toCommonJS(exports_Payment));marketplaceServices=createMarketplaceServices2({options:resolvedOptions,logger:logger2,getDb:()=>db,provider:paymentService.providerInstance,schemaTables})}let paymentRoutes=createPaymentRoutes2({provider:paymentService.providerInstance,webhookSecret:paymentService.webhookSecret,getPayoutService:()=>marketplaceServices?.payoutService??null,basePath:paymentConfig?.basePath||"/payment",defaultCurrency:paymentConfig?.defaultCurrency||"TRY",defaultLocale:paymentConfig?.defaultLocale||"tr",successRedirectUrl:paymentConfig?.successRedirectUrl||"/payment/success",failedRedirectUrl:paymentConfig?.failedRedirectUrl||"/payment/failed",errorRedirectUrl:paymentConfig?.errorRedirectUrl||"/payment/error",callbackUrl:paymentConfig?.callbackUrl,savedMethodsEnabled:paymentConfig?.savedMethodsEnabled??!0,threeDSecureEnabled:paymentConfig?.threeDSecureEnabled??!0,subMerchantsEnabled:paymentConfig?.subMerchantsEnabled??!1,transactionsTable:schemaTables.paymentTransactions??schemaTables.payment_transactions,methodsTable:schemaTables.paymentMethods??schemaTables.payment_methods,webhookLogsTable:schemaTables.paymentWebhookLogs??schemaTables.payment_webhook_logs,subMerchantsTable:schemaTables.paymentSubMerchants??schemaTables.payment_sub_merchants,commissionSplitsTable:schemaTables.paymentCommissionSplits??schemaTables.payment_commission_splits,productsTable:schemaTables.paymentProducts??schemaTables.payment_products,pricesTable:schemaTables.paymentPrices??schemaTables.payment_prices,customersTable:schemaTables.paymentCustomers??schemaTables.payment_customers,subscriptionsTable:schemaTables.paymentSubscriptions??schemaTables.payment_subscriptions,invoicesTable:schemaTables.paymentInvoices??schemaTables.payment_invoices,db,logger:logger2});if(plugin.use(paymentRoutes),logger2.info("[Payment] Routes registered",{basePath:paymentConfig?.basePath||"/payment",provider:paymentService.provider}),marketplaceServices){let{createMarketplaceRoutes:createMarketplaceRoutes2}=(init_marketplace(),__toCommonJS(exports_marketplace)),services=marketplaceServices;createMarketplaceRoutes2(plugin,{getMarketplaceService:()=>services.marketplaceService,getPayoutService:()=>services.payoutService,basePath:paymentConfig?.basePath||"/payment",logger:logger2}),logger2.info("[Payment] Marketplace routes registered",{basePath:`${paymentConfig?.basePath||"/payment"}/marketplace`})}}}let cohortConfig=authentication?.cohorts;if(cohortConfig?.enabled!==!1&&!isConsumerMode&&db){let cohortBasePath=cohortConfig?.basePath||"/auth/admin/cohorts",resolveTable=(key2,fallback)=>schemaTables[key2]??(fallback?schemaTables[fallback]:null)??null,resolvedCohortsTable=resolveTable("userCohorts","user_cohorts");if(!resolvedCohortsTable)logger2.warn("[Cohort] user_cohorts table not found in schema \u2014 cohort routes will be inactive. Re-generate schema with nucleus-core >= 0.9.120");if(plugin.use(createCohortRoutes({db,logger:logger2,cohortsTable:resolvedCohortsTable,usersTable:resolveTable("users"),rolesTable:resolveTable("roles"),userRolesTable:resolveTable("userRoles","user_roles"),profilesTable:resolveTable("profiles"),basePath:cohortBasePath,get defaultRole(){return resolvedOptions.authentication?.defaultRole},get defaultRoles(){return resolvedOptions.authentication?.defaultRoles},emailExemptDomains:authentication?.emailExemptDomains,passwordPolicy:authentication?.passwordPolicy})),resolvedCohortsTable)logger2.info("[Cohort] Routes registered",{basePath:cohortBasePath})}if(resolvedOptions.configManagement?.enabled){let configBasePath=resolvedOptions.configManagement.basePath||"/nucleus/config";plugin.use(createConfigRoutes({logger:logger2,resolvedOptions,configFilePath,basePath:configBasePath,db,schemaTables,getRedis:()=>{let redis=getRedisManager();if(!redis)return null;return{read:async(key2)=>{let r2=await redis.read(key2);return{success:r2.success,data:r2.success?r2.data:null}},create:async(key2,value2)=>{return{success:(await redis.create(key2,value2)).success}}}},onConfigUpdate:(section,_newValue)=>{logger2.info(`[ConfigManagement] Section "${section}" updated in-memory`,{section})}})),logger2.info(`[ConfigManagement] Routes enabled at ${configBasePath}`)}if(resolvedOptions.secrets?.enabled){let secretsBasePath=resolvedOptions.secrets.basePath||"/secrets";warnOnEntityBasePathCollision("Secrets",secretsBasePath,resolvedOptions.entities,logger2),plugin.use(createSecretsRoutes({logger:logger2,basePath:secretsBasePath,db,schemaTables,resolvedOptions,getService:()=>secretsService,getEmailService,getStorageProvider:()=>storageProvider,buildStorageProvider,adminRoles:resolvedOptions.secrets.adminRoles})),logger2.info(`[Secrets] Routes enabled at ${secretsBasePath}`)}if(resolvedOptions.integrations?.enabled){let integrationsBasePath=resolvedOptions.integrations.basePath||"/integrations";warnOnEntityBasePathCollision("Integrations",integrationsBasePath,resolvedOptions.entities,logger2),plugin.use(createIntegrationRoutes({logger:logger2,basePath:integrationsBasePath,db,schemaTables,resolvedOptions,getEncryptionKey:()=>resolveMasterKey(resolvedOptions.secrets?.encryptionKey)??null,getLockStore:()=>getRedisManager(),publish:(topic,payload)=>pubsubClientManager?.broadcastEvent(topic,payload),adminRoles:resolvedOptions.integrations.adminRoles}))}plugin.onStart((app)=>{let requiredBody=requiredBodyCeiling({storageEnabled:resolvedOptions.storage?.enabled,maxFileSizeBytes:resolvedOptions.storage?.maxFileSizeBytes,maxFiles:resolvedOptions.storage?.formData?.maxFiles}),complaint=bodyCeilingComplaint({configured:app.config?.serve?.maxRequestBodySize,required:requiredBody});if(complaint)logger2.error(`[Storage] ${complaint}`);let memoryComplaint=memoryCeilingComplaint({required:requiredBody,cgroupLimit:readCgroupLimitBytes()});if(memoryComplaint)logger2.error(`[Storage] ${memoryComplaint}`);let memoryAdvice=readRuntimeMemoryAdvice();if(memoryAdvice){let mib=(bytes)=>`${Math.round(bytes/1024/1024)} MiB`;logger2.warn(`[Runtime] The JS engine sizes its heap for ${mib(memoryAdvice.hostTotal)} but this container is capped at ${mib(memoryAdvice.cgroupLimit)} \u2014 it feels no memory `+"pressure and may grow past the limit and be OOM-killed. Raising the container memory limit delays this; no runtime option we have measured prevents it.")}let port=Number(process.env.PORT)||3000,appId=resolvedOptions.appId||"nucleus",mode=resolvedOptions.mode||"production";console.log(""),console.log(` \x1B[32m\uD83D\uDE80 ${appId}\x1B[0m \x1B[90mv${Date.now()}\x1B[0m`),console.log(` \x1B[36m\u279C\x1B[0m Local: \x1B[36mhttp://localhost:${port}\x1B[0m`),console.log(` \x1B[36m\u279C\x1B[0m Mode: \x1B[33m${mode}\x1B[0m`),console.log("")});let shuttingDown=!1,drainResources=async(trigger)=>{if(shuttingDown)return;shuttingDown=!0,logger2.info(`[Shutdown] ${trigger} \u2014 draining resources`);try{liveMonitoringService?.stop(),monitoringService?.stop(),secretsService?.stop(),storageProvider?.close()}catch(err){logger2.warn("[Shutdown] failed stopping monitoring",{error:err instanceof Error?err.message:String(err)})}try{let redisClient=getRedisManager()?.getDirectClient?.();if(redisClient)await redisClient.quit()}catch(err){logger2.warn("[Shutdown] failed closing Redis",{error:err instanceof Error?err.message:String(err)})}try{for(let timer of backgroundIntervals)clearInterval(timer);backgroundIntervals.length=0}catch(err){logger2.warn("[Shutdown] failed clearing background intervals",{error:err instanceof Error?err.message:String(err)})}try{let{destroyAckCleanup:destroyAckCleanup2}=await Promise.resolve().then(() => (init_ack_manager(),exports_ack_manager));destroyAckCleanup2()}catch{}try{if(dbPool)await dbPool.end()}catch(err){logger2.warn("[Shutdown] failed draining DB pool",{error:err instanceof Error?err.message:String(err)})}logger2.info("[Shutdown] complete")};if(plugin.onStop(async()=>{await drainResources("Elysia onStop")}),resolvedOptions.gracefulShutdown!==!1){let onSignal=(signal)=>{drainResources(signal).finally(()=>process.exit(0))};process.once("SIGTERM",()=>onSignal("SIGTERM")),process.once("SIGINT",()=>onSignal("SIGINT"))}return plugin}init_AzureEmailService();init_Gmail();export{usePubSubStore,usePubSub,serverFetch,requiredBodyCeiling,parseCron2 as parseCron,matchesCron,isDue,generateVerificationEndpoints,generateTenantEndpoints,generateSystemTableEndpoints,generateMonitoringEndpoints,generateMarketplaceEndpoints,generateEndpointsFromConfig,generateDomainEndpoints,generateCohortEndpoints,generateChatEndpoints,generateAuthEndpoints,generateAllEndpoints,generateAdminEndpoints,describeCron,decodeHeaderList,createServerFactory,createApiHook,VERIFICATION_ENDPOINTS,TENANT_ENDPOINTS,ServerFetch,SYSTEM_TABLES,PAYMENT_ENDPOINTS,NucleusElysiaPlugin,MONITORING_ENDPOINTS,MARKETPLACE_ENDPOINTS,GmailService,DOMAIN_ENDPOINTS,CONFIG_ENDPOINTS,COHORT_ENDPOINTS,CHAT_ENDPOINTS,AzureEmailService,AUTH_ENDPOINT_CONFIGS,AUTH_ENDPOINTS};
@@ -52,8 +52,6 @@ export interface RuntimeMemoryAdvice {
52
52
  cgroupLimit: number;
53
53
  /** What the JS engine believes it has — the NODE's RAM. */
54
54
  hostTotal: number;
55
- /** What `BUN_JSC_forceRAMSize` should be set to, in bytes. */
56
- recommended: number;
57
55
  }
58
56
  /**
59
57
  * The gap between the ceiling the kernel enforces and the one the engine believes.
@@ -69,20 +67,20 @@ export interface RuntimeMemoryAdvice {
69
67
  *
70
68
  * It cannot be fixed from inside the process — JSC reads its options when the
71
69
  * runtime boots, long before any of this code runs. So the honest thing is to
72
- * SAY it at startup, with the number to set, rather than let a pod quietly
73
- * restart itself twice a day.
70
+ * SAY it at startup rather than let a pod quietly restart itself twice a day.
74
71
  *
75
- * A quarter of the limit, not the whole thing: the JS heap is a minority of RSS
76
- * in this stack. On the same install, live objects sat at 60-90 MB while total
77
- * RSS reached a gigabyte the rest is native buffers, the log ring, the pg and
78
- * redis clients. Handing the engine the entire limit just moves the OOM; a hint
79
- * ABOVE what the heap ever reaches is a brake whose pedal is never touched, and
80
- * that was measured too: 768 MiB on a 1 GiB pod behaved identically to no hint
81
- * at all.
72
+ * This used to carry a remedy: set `BUN_JSC_forceRAMSize` to a quarter of the
73
+ * limit. That remedy is GONE. Measured inside a live container on 15 Aug 2026
74
+ * (Bun 1.3.10), across three settings and two workloads garbage-heavy and
75
+ * live-heavy the option moved peak RSS by under 1 MB; quartering it and
76
+ * removing it entirely gave the same figure. The probe was not blind (it saw
77
+ * RSS fall 150 MB 35 MB on the garbage run), and the option IS recognised (a
78
+ * bogus `BUN_JSC_*` name is still rejected at startup). It simply does nothing
79
+ * here. A remedy that does not work is worse than none: it reads as done, it
80
+ * costs a deploy, and it stops the search.
82
81
  *
83
- * Returns `null` when there is nothing to say: uncapped container, a "limit"
84
- * that is not really a limit, or an operator who has already set a hint at or
85
- * below the advice.
82
+ * Returns `null` when there is nothing to say: an uncapped container, or a
83
+ * "limit" that is not really a limit.
86
84
  */
87
85
  export declare function runtimeMemoryAdvice(input: {
88
86
  cgroupLimit: number | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nucleus-core-ts",
3
- "version": "0.9.962",
3
+ "version": "0.9.963",
4
4
  "description": "Production-ready, enterprise-grade TypeScript framework for building multi-tenant APIs",
5
5
  "author": "Hidayet Can Özcan <hidayetcan@gmail.com>",
6
6
  "license": "SEE LICENSE IN LICENSE",