nucleus-core-ts 0.9.946 → 0.9.947
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.
|
|
1
|
+
0.9.947
|
package/dist/index.js
CHANGED
|
@@ -71,7 +71,7 @@ and limitations under the License.
|
|
|
71
71
|
</div>
|
|
72
72
|
</body>
|
|
73
73
|
</html>
|
|
74
|
-
`}getActiveAlerts(){return Array.from(this.state.activeAlerts.values())}acknowledgeAlert(alertId){for(let[_type,alert]of this.state.activeAlerts)if(alert.id===alertId)return alert.acknowledged=!0,!0;return!1}clearAlert(type){this.state.activeAlerts.delete(type)}}var init_AlertService=()=>{};class ApplicationCollector{config;requestCount=0;responseTimes=[];errorCount=0;rateLimitBlocks=0;byEndpoint={};byMethod={};byStatus={};byErrorType={};lastCollectTime=Date.now();constructor(config){this.config=config}recordRequest(params){if(!this.config?.enabled)return;
|
|
74
|
+
`}getActiveAlerts(){return Array.from(this.state.activeAlerts.values())}acknowledgeAlert(alertId){for(let[_type,alert]of this.state.activeAlerts)if(alert.id===alertId)return alert.acknowledged=!0,!0;return!1}clearAlert(type){this.state.activeAlerts.delete(type)}}var init_AlertService=()=>{};class ApplicationCollector{config;requestCount=0;responseTimes=[];errorCount=0;rateLimitBlocks=0;byEndpoint={};byMethod={};byStatus={};byErrorType={};lastCollectTime=Date.now();buckets=[];constructor(config){this.config=config}bucketFor(second){let index=second%60,bucket=this.buckets[index];if(!bucket)return bucket={second,requests:0,errors:0,blocks:0},this.buckets[index]=bucket,bucket;if(bucket.second!==second)bucket.second=second,bucket.requests=0,bucket.errors=0,bucket.blocks=0;return bucket}windowed(pick){let oldest=Math.floor(Date.now()/1000)-60,total=0;for(let bucket of this.buckets)if(bucket&&bucket.second>oldest)total+=pick(bucket);return total}recordRequest(params){if(!this.config?.enabled)return;this.requestCount++;let bucket=this.bucketFor(Math.floor(Date.now()/1000));if(bucket.requests+=1,params.isError)bucket.errors+=1;if(this.config.metrics?.responseTime!==!1){if(this.responseTimes.push(params.responseTimeMs),this.responseTimes.length>1e4)this.responseTimes=this.responseTimes.slice(-5000)}if(this.config.metrics?.requests!==!1)this.byEndpoint[params.endpoint]=(this.byEndpoint[params.endpoint]||0)+1,this.byMethod[params.method]=(this.byMethod[params.method]||0)+1,this.byStatus[String(params.status)]=(this.byStatus[String(params.status)]||0)+1;if(this.config.metrics?.errors!==!1&¶ms.isError){if(this.errorCount++,params.errorType)this.byErrorType[params.errorType]=(this.byErrorType[params.errorType]||0)+1}}recordRateLimitBlock(){if(!this.config?.enabled||this.config.metrics?.rateLimits===!1)return;this.rateLimitBlocks++,this.bucketFor(Math.floor(Date.now()/1000)).blocks+=1}collect(){if(!this.config?.enabled)return null;let perMinute=this.windowed((b)=>b.requests),blockedPerMinute=this.windowed((b)=>b.blocks),sortedTimes=[...this.responseTimes].sort((a,b)=>a-b),len=sortedTimes.length;return{requests:{total:this.requestCount,perMinute,byEndpoint:{...this.byEndpoint},byMethod:{...this.byMethod},byStatus:{...this.byStatus}},responseTime:{avg:len>0?Math.round(sortedTimes.reduce((a,b)=>a+b,0)/len*100)/100:0,min:len>0?sortedTimes[0]??0:0,max:len>0?sortedTimes[len-1]??0:0,p50:len>0?sortedTimes[Math.floor(len*0.5)]??0:0,p95:len>0?sortedTimes[Math.floor(len*0.95)]??0:0,p99:len>0?sortedTimes[Math.floor(len*0.99)]??0:0},errors:{total:this.errorCount,rate:(()=>{let requests=perMinute;if(requests===0)return 0;let errors=this.windowed((b)=>b.errors);return Math.round(errors/requests*100*100)/100})(),byType:{...this.byErrorType}},rateLimits:{blocked:this.rateLimitBlocks,blockedPerMinute}}}reset(){this.requestCount=0,this.responseTimes=[],this.errorCount=0,this.rateLimitBlocks=0,this.byEndpoint={},this.byMethod={},this.byStatus={},this.byErrorType={},this.buckets=[],this.lastCollectTime=Date.now()}getRequestsPerMinute(){return this.windowed((b)=>b.requests)}getErrorRate(){let requests=this.windowed((b)=>b.requests);return requests>0?this.windowed((b)=>b.errors)/requests*100:0}getRateLimitBlocksPerMinute(){return this.windowed((b)=>b.blocks)}getAvgResponseTime(){if(this.responseTimes.length===0)return 0;return this.responseTimes.reduce((a,b)=>a+b,0)/this.responseTimes.length}}class DatabaseCollector{config;execute;lastTotals=null;activeTimeSupported=!0;constructor(config,execute){this.config=config,this.execute=execute}async collect(){if(!this.config.enabled)return null;let metrics={connections:{active:0,idle:0,total:0},queries:{total:0,avgTime:0,slowQueries:0}};if(this.config.metrics?.connections!==!1)metrics.connections=await this.collectConnections();if(this.config.metrics?.queryTime!==!1)metrics.queries=await this.collectQueries();return metrics}toNumber(value){let num=Number.parseFloat(String(value??0));return Number.isNaN(num)?0:num}async collectConnections(){try{let row=(await this.execute(`SELECT
|
|
75
75
|
count(*) FILTER (WHERE state = 'active') AS active,
|
|
76
76
|
count(*) FILTER (WHERE state = 'idle') AS idle,
|
|
77
77
|
count(*) AS total
|
|
@@ -10,7 +10,34 @@ export declare class ApplicationCollector {
|
|
|
10
10
|
private byStatus;
|
|
11
11
|
private byErrorType;
|
|
12
12
|
private lastCollectTime;
|
|
13
|
+
/**
|
|
14
|
+
* The last sixty seconds, one bucket each.
|
|
15
|
+
*
|
|
16
|
+
* ## What this replaces, and why it is not a detail
|
|
17
|
+
*
|
|
18
|
+
* `perMinute` used to be `requestCount / minutesSinceBoot` — the lifetime
|
|
19
|
+
* AVERAGE, presented as the current rate, on a chart labelled "requests per
|
|
20
|
+
* minute" and in the alert thresholds. `reset()` exists to make that
|
|
21
|
+
* arithmetic correct and is called from nowhere, so the divisor kept growing
|
|
22
|
+
* for as long as the pod lived.
|
|
23
|
+
*
|
|
24
|
+
* What that does to a real number: measured on 6 August 2026, a load run
|
|
25
|
+
* driving roughly fifty API requests a SECOND was recorded as a peak of 273
|
|
26
|
+
* per MINUTE, in the same snapshot that reported 370 cumulative hits on the
|
|
27
|
+
* busiest endpoint alone — two figures that cannot both be true. A pod up for
|
|
28
|
+
* a week would have flattened a genuine incident into nothing at all, which
|
|
29
|
+
* is the same failure in the direction that matters: the alert that never
|
|
30
|
+
* fires.
|
|
31
|
+
*
|
|
32
|
+
* A rolling window costs sixty small objects and answers the question the
|
|
33
|
+
* metric's name promises.
|
|
34
|
+
*/
|
|
35
|
+
private buckets;
|
|
13
36
|
constructor(config: MonitoringConfig['application']);
|
|
37
|
+
/** The bucket for `now`, recycled if this slot belongs to an expired second. */
|
|
38
|
+
private bucketFor;
|
|
39
|
+
/** Sums the buckets still inside the window. */
|
|
40
|
+
private windowed;
|
|
14
41
|
recordRequest(params: {
|
|
15
42
|
endpoint: string;
|
|
16
43
|
method: string;
|
|
@@ -21,8 +48,21 @@ export declare class ApplicationCollector {
|
|
|
21
48
|
}): void;
|
|
22
49
|
recordRateLimitBlock(): void;
|
|
23
50
|
collect(): ApplicationMetrics | null;
|
|
51
|
+
/**
|
|
52
|
+
* Clears the cumulative counters.
|
|
53
|
+
*
|
|
54
|
+
* Kept for callers that want a fresh baseline, but nothing on the boot path
|
|
55
|
+
* calls it — and that used to be a silent bug rather than a choice, because
|
|
56
|
+
* the rate arithmetic depended on it. The rates no longer do: they read the
|
|
57
|
+
* rolling window, which expires on its own.
|
|
58
|
+
*/
|
|
24
59
|
reset(): void;
|
|
60
|
+
/** Requests in the last sixty seconds. */
|
|
25
61
|
getRequestsPerMinute(): number;
|
|
62
|
+
/**
|
|
63
|
+
* Error rate over the last minute — the same window the alert thresholds
|
|
64
|
+
* should be judging, and the same one `collect()` reports.
|
|
65
|
+
*/
|
|
26
66
|
getErrorRate(): number;
|
|
27
67
|
getRateLimitBlocksPerMinute(): number;
|
|
28
68
|
getAvgResponseTime(): number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nucleus-core-ts",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.947",
|
|
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",
|