web_plsql 1.2.0 → 1.3.1

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.
Files changed (119) hide show
  1. package/README.md +103 -8
  2. package/dist/chunk-DQk6qfdC.mjs +18 -0
  3. package/dist/frontend/assets/main-C8GKnEhz.js +147 -0
  4. package/dist/frontend/assets/main-C8GKnEhz.js.br +0 -0
  5. package/dist/frontend/assets/main-C8GKnEhz.js.gz +0 -0
  6. package/dist/frontend/assets/main-bU4x_yEW.css +1 -0
  7. package/dist/frontend/assets/main-bU4x_yEW.css.br +0 -0
  8. package/dist/frontend/assets/main-bU4x_yEW.css.gz +0 -0
  9. package/{src/admin → dist/frontend}/index.html +12 -25
  10. package/dist/frontend/index.html.br +0 -0
  11. package/dist/frontend/index.html.gz +0 -0
  12. package/dist/index.d.mts +646 -0
  13. package/dist/index.mjs +3559 -0
  14. package/dist/index.mjs.map +1 -0
  15. package/dist/oracledb-mock-Dn8aHtd7.mjs +68 -0
  16. package/dist/oracledb-mock-Dn8aHtd7.mjs.map +1 -0
  17. package/examples/config-apex.js +1 -1
  18. package/examples/config-docker.js +1 -2
  19. package/package.json +48 -32
  20. package/examples/config-native.js +0 -68
  21. package/src/admin/client/charts.ts +0 -598
  22. package/src/admin/client/main.js +0 -3
  23. package/src/admin/client/tailwind.css +0 -41
  24. package/src/admin/js/api.ts +0 -170
  25. package/src/admin/js/app.ts +0 -614
  26. package/src/admin/js/eslint.config.js +0 -74
  27. package/src/admin/js/schemas.ts +0 -215
  28. package/src/admin/js/templates/config.ts +0 -147
  29. package/src/admin/js/templates/errorRow.ts +0 -21
  30. package/src/admin/js/templates/index.ts +0 -4
  31. package/src/admin/js/templates/poolCard.ts +0 -61
  32. package/src/admin/js/templates/traceRow.ts +0 -24
  33. package/src/admin/js/tsconfig.json +0 -24
  34. package/src/admin/js/types.ts +0 -336
  35. package/src/admin/js/ui/components.ts +0 -44
  36. package/src/admin/js/ui/table.ts +0 -173
  37. package/src/admin/js/ui/theme.ts +0 -93
  38. package/src/admin/js/ui/views.ts +0 -427
  39. package/src/admin/js/util/format.ts +0 -46
  40. package/src/admin/js/util/metrics.ts +0 -24
  41. package/src/admin/lib/chart.bundle.css +0 -1
  42. package/src/admin/lib/chart.bundle.js +0 -146
  43. package/src/admin/style.css +0 -1437
  44. package/src/bin/load-test.js +0 -202
  45. package/src/handler/handlerAdmin.js +0 -293
  46. package/src/handler/handlerLogger.js +0 -21
  47. package/src/handler/handlerUpload.js +0 -26
  48. package/src/handler/plsql/cgi.js +0 -131
  49. package/src/handler/plsql/errorPage.js +0 -113
  50. package/src/handler/plsql/handlerPlSql.js +0 -90
  51. package/src/handler/plsql/owaPageStream.js +0 -93
  52. package/src/handler/plsql/parsePage.js +0 -213
  53. package/src/handler/plsql/procedure.js +0 -426
  54. package/src/handler/plsql/procedureError.js +0 -40
  55. package/src/handler/plsql/procedureNamed.js +0 -282
  56. package/src/handler/plsql/procedureSanitize.js +0 -286
  57. package/src/handler/plsql/procedureVariable.js +0 -56
  58. package/src/handler/plsql/request.js +0 -124
  59. package/src/handler/plsql/requestError.js +0 -23
  60. package/src/handler/plsql/sendResponse.js +0 -174
  61. package/src/handler/plsql/stream.js +0 -28
  62. package/src/handler/plsql/upload.js +0 -109
  63. package/src/index.js +0 -19
  64. package/src/server/adminContext.js +0 -23
  65. package/src/server/config.js +0 -57
  66. package/src/server/connections.js +0 -40
  67. package/src/server/server.js +0 -304
  68. package/src/types.js +0 -150
  69. package/src/util/cache.js +0 -123
  70. package/src/util/errorToString.js +0 -24
  71. package/src/util/file.js +0 -91
  72. package/src/util/html.js +0 -53
  73. package/src/util/jsonLogger.js +0 -47
  74. package/src/util/oracle.js +0 -80
  75. package/src/util/shutdown.js +0 -44
  76. package/src/util/statsManager.js +0 -368
  77. package/src/util/trace.js +0 -326
  78. package/src/util/traceManager.js +0 -68
  79. package/src/util/type.js +0 -6
  80. package/src/util/util.js +0 -66
  81. package/src/version.js +0 -5
  82. package/types/admin/js/schemas.d.ts +0 -384
  83. package/types/admin/js/types.d.ts +0 -312
  84. package/types/handler/handlerAdmin.d.ts +0 -79
  85. package/types/handler/handlerLogger.d.ts +0 -2
  86. package/types/handler/handlerUpload.d.ts +0 -7
  87. package/types/handler/plsql/cgi.d.ts +0 -4
  88. package/types/handler/plsql/errorPage.d.ts +0 -11
  89. package/types/handler/plsql/handlerPlSql.d.ts +0 -13
  90. package/types/handler/plsql/owaPageStream.d.ts +0 -28
  91. package/types/handler/plsql/parsePage.d.ts +0 -3
  92. package/types/handler/plsql/procedure.d.ts +0 -13
  93. package/types/handler/plsql/procedureError.d.ts +0 -23
  94. package/types/handler/plsql/procedureNamed.d.ts +0 -14
  95. package/types/handler/plsql/procedureSanitize.d.ts +0 -11
  96. package/types/handler/plsql/procedureVariable.d.ts +0 -10
  97. package/types/handler/plsql/request.d.ts +0 -9
  98. package/types/handler/plsql/requestError.d.ts +0 -8
  99. package/types/handler/plsql/sendResponse.d.ts +0 -6
  100. package/types/handler/plsql/upload.d.ts +0 -5
  101. package/types/index.d.ts +0 -10
  102. package/types/server/adminContext.d.ts +0 -17
  103. package/types/server/config.d.ts +0 -2
  104. package/types/server/server.d.ts +0 -57
  105. package/types/types.d.ts +0 -328
  106. package/types/util/cache.d.ts +0 -69
  107. package/types/util/errorToString.d.ts +0 -1
  108. package/types/util/file.d.ts +0 -6
  109. package/types/util/html.d.ts +0 -3
  110. package/types/util/jsonLogger.d.ts +0 -45
  111. package/types/util/oracle.d.ts +0 -5
  112. package/types/util/shutdown.d.ts +0 -2
  113. package/types/util/statsManager.d.ts +0 -395
  114. package/types/util/trace.d.ts +0 -22
  115. package/types/util/traceManager.d.ts +0 -35
  116. package/types/util/type.d.ts +0 -1
  117. package/types/util/util.d.ts +0 -3
  118. package/types/version.d.ts +0 -1
  119. /package/{src/admin/favicon.svg → dist/frontend/assets/favicon-mQAM4tVu.svg} +0 -0
package/src/util/html.js DELETED
@@ -1,53 +0,0 @@
1
- /*
2
- * Html utilities
3
- */
4
-
5
- /**
6
- * Escape html string.
7
- *
8
- * @param {string} value - The value.
9
- * @returns {string} - The escaped value.
10
- */
11
- export const escapeHtml = (value) => value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
12
-
13
- /**
14
- * Convert LF and/or CR to <br>
15
- * @param {string} text - The text to convert.
16
- * @returns {string} - The converted text.
17
- */
18
- export const convertAsciiToHtml = (text) => {
19
- let html = escapeHtml(text);
20
-
21
- html = html.replace(/(?:\r\n|\r|\n)/g, '<br />');
22
- html = html.replace(/\t/g, '&nbsp;&nbsp;&nbsp;');
23
-
24
- return html;
25
- };
26
-
27
- /**
28
- * get a minimal html page.
29
- * @param {string} body - The body.
30
- * @returns {string} - The html page.
31
- */
32
- export const getHtmlPage = (body) => `<!DOCTYPE html>
33
- <html lang="en">
34
- <head>
35
- <meta charset="utf-8">
36
- <title>web_plsql error page</title>
37
- <style type="text/css">
38
- html {
39
- font-family: monospace, sans-serif;
40
- font-size: 12px;
41
- }
42
- h1 {
43
- font-size: 16px;
44
- padding: 2px;
45
- background-color: #cc0000;
46
- }
47
- </style>
48
- </head>
49
- <body>
50
- ${body}
51
- </body>
52
- </html>
53
- `;
@@ -1,47 +0,0 @@
1
- import * as rotatingFileStream from 'rotating-file-stream';
2
-
3
- /**
4
- * @typedef {object} LogEntry
5
- * @property {string} timestamp - ISO string
6
- * @property {'error'|'info'|'warning'} type - Log level
7
- * @property {string} message - Error message
8
- * @property {object} [req] - Request details
9
- * @property {object} [details] - Additional details (stack, sql, etc)
10
- */
11
-
12
- export class JsonLogger {
13
- constructor(filename = 'error.json.log') {
14
- this.stream = rotatingFileStream.createStream(filename, {
15
- size: '10M', // rotate every 10 MegaBytes written
16
- interval: '1d', // rotate daily
17
- maxFiles: 10, // maximum number of rotated files to keep
18
- compress: 'gzip', // compress rotated files
19
- });
20
- }
21
-
22
- /**
23
- * Log an entry as NDJSON.
24
- * @param {LogEntry} entry - The entry to log.
25
- */
26
- log(entry) {
27
- try {
28
- // Ensure timestamp exists
29
- if (!entry.timestamp) {
30
- entry.timestamp = new Date().toISOString();
31
- }
32
- const line = JSON.stringify(entry);
33
- this.stream.write(line + '\n');
34
- } catch (err) {
35
- console.error('JsonLogger: Failed to write log', err);
36
- }
37
- }
38
-
39
- /**
40
- * Close the stream.
41
- */
42
- close() {
43
- this.stream.end();
44
- }
45
- }
46
-
47
- export const jsonLogger = new JsonLogger();
@@ -1,80 +0,0 @@
1
- import debugModule from 'debug';
2
- const debug = debugModule('webplsql:oracle');
3
-
4
- import oracledb from 'oracledb';
5
-
6
- /**
7
- * @typedef {import('oracledb').Pool} Pool
8
- */
9
-
10
- /**
11
- * Test is the connection is valid.
12
- * @param {Pool} connectionPool - database connection
13
- * @returns {Promise<boolean>} - success
14
- */
15
- export const connectionValid = async (connectionPool) => {
16
- let connection;
17
- try {
18
- connection = await connectionPool.getConnection();
19
- debug('connection valid');
20
- return true;
21
- } catch (err) {
22
- debug('connection broken', err);
23
- return false;
24
- } finally {
25
- if (connection) {
26
- await connection.release();
27
- }
28
- }
29
- };
30
-
31
- /**
32
- * Allocate the Oracle database pool.
33
- * @param {string} user - The database user name.
34
- * @param {string} password - The password of the database user.
35
- * @param {string} connectString - The Oracle database instance to connect to. The string can be an Easy Connect string, or a Net Service Name from a tnsnames.ora file, or the name of a local Oracle database instance.
36
- * @param {number} poolMin - The minimum number of connections a connection pool maintains.
37
- * @param {number} poolMax - The maximum number of connections to which a connection pool can grow.
38
- * @returns {Promise<Pool>} - The connection pool.
39
- */
40
- export const poolCreate = async (user, password, connectString, poolMin = 10, poolMax = 1000) => {
41
- const pool = await oracledb.createPool({
42
- user, // The database user name.
43
- password, // The password of the database user.
44
- connectString, // The Oracle database instance to connect to. The string can be an Easy Connect string, or a Net Service Name from a tnsnames.ora file, or the name of a local Oracle database instance.
45
- poolMin, // The minimum number of connections a connection pool maintains, even when there is no activity to the target database.
46
- poolMax, // The maximum number of connections to which a connection pool can grow.
47
- poolIncrement: 10, // The number of connections that are opened whenever a connection request exceeds the number of currently open connections.
48
- queueTimeout: 1000, // The number of milliseconds after which connection requests waiting in the connection request queue are terminated. If queueTimeout is 0, then queued connection requests are never terminated.
49
- });
50
-
51
- const valid = await connectionValid(pool);
52
- if (!valid) {
53
- await poolClose(pool);
54
- throw new Error(`Unable to connect with Oracle Database as ${user}@${connectString}`);
55
- }
56
-
57
- return pool;
58
- };
59
-
60
- /**
61
- * Close the Oracle database pool.
62
- * @param {Pool} pool - The connection pool.
63
- * @returns {Promise<void>}
64
- */
65
- export const poolClose = async (pool) => {
66
- try {
67
- await pool.close(0);
68
- } catch (err) {
69
- debug('Cannot close pool', err);
70
- }
71
- };
72
-
73
- /**
74
- * Close the Oracle database pools.
75
- * @param {Pool[]} pools - The connection pools.
76
- * @returns {Promise<void>}
77
- */
78
- export const poolsClose = async (pools) => {
79
- await Promise.all(pools.map(poolClose));
80
- };
@@ -1,44 +0,0 @@
1
- import debugModule from 'debug';
2
- const debug = debugModule('webplsql:shutdown');
3
-
4
- /**
5
- * Install a shutdown handler.
6
- * @param {() => Promise<void>} handler - Shutdown handler
7
- * @returns {void}
8
- */
9
- export const installShutdown = (handler) => {
10
- debug('installShutdown');
11
-
12
- /*
13
- * The 'unhandledRejection' event is emitted whenever a Promise is rejected and no error handler is attached to the promise within a turn of the event loop.
14
- */
15
- process.on('unhandledRejection', (reason) => {
16
- if (reason instanceof Error) {
17
- console.error(`\n${reason.message}. Graceful shutdown...`);
18
- } else {
19
- console.error('\nUnhandled promise rejection. Graceful shutdown...', reason);
20
- }
21
- void handler();
22
- });
23
-
24
- // install signal event handler
25
- process.on('SIGTERM', function sigterm() {
26
- console.log('\nGot SIGINT (aka ctrl-c in docker). Graceful shutdown...');
27
- void handler();
28
- });
29
-
30
- process.on('SIGINT', function sigint() {
31
- console.log('\nGot SIGTERM (aka docker container stop). Graceful shutdown...');
32
- void handler();
33
- });
34
- };
35
-
36
- /**
37
- * Force a shutdown.
38
- * @returns {void}
39
- */
40
- export const forceShutdown = () => {
41
- debug('forceShutdown');
42
-
43
- process.kill(process.pid, 'SIGTERM');
44
- };
@@ -1,368 +0,0 @@
1
- import debugModule from 'debug';
2
- import os from 'node:os';
3
-
4
- const debug = debugModule('webplsql:statsManager');
5
-
6
- /**
7
- * @typedef {object} StatsConfig
8
- * @property {number} intervalMs - Duration of each statistical bucket (default: 5000ms).
9
- * @property {number} maxHistoryPoints - Number of buckets to keep in the ring buffer (default: 200).
10
- * @property {boolean} sampleSystem - Whether to automatically sample CPU/Memory (default: true).
11
- * @property {boolean} samplePools - Whether to automatically sample Oracle pool utilization (default: true).
12
- * @property {number} percentilePrecision - Max number of samples per bucket for P95/P99 calculation (default: 1000).
13
- */
14
-
15
- /**
16
- * @typedef {object} CacheStats
17
- * @property {number} size - Number of entries.
18
- * @property {number} hits - Number of hits.
19
- * @property {number} misses - Number of misses.
20
- */
21
-
22
- /**
23
- * @typedef {object} PoolCacheSnapshot
24
- * @property {CacheStats} procedureName - Procedure name cache stats.
25
- * @property {CacheStats} argument - Argument cache stats.
26
- */
27
-
28
- /**
29
- * @typedef {object} PoolSnapshot
30
- * @property {string} name - The pool name.
31
- * @property {number} connectionsInUse - Number of active connections.
32
- * @property {number} connectionsOpen - Number of open connections.
33
- * @property {PoolCacheSnapshot} [cache] - Cache statistics.
34
- */
35
-
36
- /**
37
- * @typedef {object} Bucket
38
- * @property {number} timestamp - End time of the bucket.
39
- * @property {number} requests - Number of requests.
40
- * @property {number} errors - Number of errors.
41
- * @property {number} durationMin - Minimum duration.
42
- * @property {number} durationMax - Maximum duration.
43
- * @property {number} durationAvg - Average duration.
44
- * @property {number} durationP95 - 95th percentile duration.
45
- * @property {number} durationP99 - 99th percentile duration.
46
- * @property {object} system - System metrics.
47
- * @property {number} system.cpu - CPU usage percentage.
48
- * @property {number} system.heapUsed - Heap used in bytes.
49
- * @property {number} system.heapTotal - Heap total in bytes.
50
- * @property {number} system.rss - RSS in bytes.
51
- * @property {number} system.external - External memory in bytes.
52
- * @property {PoolSnapshot[]} pools - Pool utilization snapshots.
53
- */
54
-
55
- /**
56
- * @typedef {object} CurrentBucket
57
- * @property {number} count - Number of requests.
58
- * @property {number} errors - Number of errors.
59
- * @property {number} durationSum - Sum of durations.
60
- * @property {number} durationMin - Minimum duration.
61
- * @property {number} durationMax - Maximum duration.
62
- * @property {number[]} durations - List of durations for percentile calculation.
63
- */
64
-
65
- /**
66
- * @typedef {object} MemoryLifetime
67
- * @property {number} heapUsedMax - Max heap used.
68
- * @property {number} heapTotalMax - Max heap total.
69
- * @property {number} rssMax - Max RSS.
70
- * @property {number} externalMax - Max external.
71
- */
72
-
73
- /**
74
- * @typedef {object} LifetimeStats
75
- * @property {number} totalRequests - Total requests.
76
- * @property {number} totalErrors - Total errors.
77
- * @property {number} minDuration - Min duration.
78
- * @property {number} maxDuration - Max duration.
79
- * @property {number} totalDuration - Total duration.
80
- * @property {number} maxRequestsPerSecond - Max requests per second.
81
- * @property {MemoryLifetime} memory - Memory extremes.
82
- * @property {object} cpu - CPU extremes.
83
- * @property {number} cpu.max - Max CPU.
84
- * @property {number} cpu.userMax - Max user CPU.
85
- * @property {number} cpu.systemMax - Max system CPU.
86
- */
87
-
88
- /**
89
- * @typedef {object} StatsSummary
90
- * @property {Date} startTime - Server start time.
91
- * @property {number} totalRequests - Total requests handled.
92
- * @property {number} totalErrors - Total errors encountered.
93
- * @property {number} avgResponseTime - Lifetime average response time.
94
- * @property {number} minResponseTime - Lifetime minimum response time.
95
- * @property {number} maxResponseTime - Lifetime maximum response time.
96
- * @property {number} maxRequestsPerSecond - Lifetime maximum requests per second.
97
- * @property {MemoryLifetime} maxMemory - Lifetime memory extremes.
98
- * @property {object} cpu - CPU extremes.
99
- * @property {number} cpu.max - Max CPU usage percentage.
100
- * @property {number} cpu.userMax - Max user CPU usage in microseconds.
101
- * @property {number} cpu.systemMax - Max system CPU usage in microseconds.
102
- */
103
-
104
- /**
105
- * Manager for statistical data collection and temporal bucketing.
106
- */
107
- export class StatsManager {
108
- /**
109
- * @param {Partial<StatsConfig>} config - Configuration.
110
- */
111
- constructor(config = {}) {
112
- /** @type {StatsConfig} */
113
- this.config = {
114
- intervalMs: 5000,
115
- maxHistoryPoints: 1000,
116
- sampleSystem: true,
117
- samplePools: true,
118
- percentilePrecision: 1000,
119
- ...config,
120
- };
121
-
122
- this.startTime = new Date();
123
-
124
- /** @type {Bucket[]} */
125
- this.history = [];
126
-
127
- /** @type {LifetimeStats} */
128
- this.lifetime = {
129
- totalRequests: 0,
130
- totalErrors: 0,
131
- minDuration: -1,
132
- maxDuration: -1,
133
- totalDuration: 0,
134
- maxRequestsPerSecond: 0,
135
- memory: {
136
- heapUsedMax: 0,
137
- heapTotalMax: 0,
138
- rssMax: 0,
139
- externalMax: 0,
140
- },
141
- cpu: {
142
- max: 0,
143
- userMax: 0,
144
- systemMax: 0,
145
- },
146
- };
147
-
148
- /** @type {CurrentBucket} */
149
- this._currentBucket = {
150
- count: 0,
151
- errors: 0,
152
- durations: [],
153
- durationSum: 0,
154
- durationMin: -1,
155
- durationMax: -1,
156
- };
157
-
158
- this._lastCpuTimes = this._getSystemCpuTimes();
159
-
160
- /** @type {ReturnType<typeof setTimeout> | undefined} */
161
- this._timer = undefined;
162
- if (this.config.sampleSystem) {
163
- this._timer = setInterval(() => {
164
- this.rotateBucket();
165
- }, this.config.intervalMs);
166
- if (this._timer && typeof this._timer.unref === 'function') {
167
- this._timer.unref();
168
- }
169
- }
170
- }
171
-
172
- /**
173
- * Reset the current bucket accumulator.
174
- * @private
175
- */
176
- _resetBucket() {
177
- this._currentBucket = {
178
- count: 0,
179
- errors: 0,
180
- durations: [],
181
- durationSum: 0,
182
- durationMin: -1,
183
- durationMax: -1,
184
- };
185
- }
186
-
187
- /**
188
- * Record a request event.
189
- * @param {number} duration - Duration in milliseconds.
190
- * @param {boolean} isError - Whether the request was an error.
191
- */
192
- recordRequest(duration, isError = false) {
193
- this.lifetime.totalRequests++;
194
- if (isError) {
195
- this.lifetime.totalErrors++;
196
- }
197
-
198
- this.lifetime.totalDuration += duration;
199
- if (this.lifetime.minDuration < 0 || duration < this.lifetime.minDuration) {
200
- this.lifetime.minDuration = duration;
201
- }
202
- if (this.lifetime.maxDuration < 0 || duration > this.lifetime.maxDuration) {
203
- this.lifetime.maxDuration = duration;
204
- }
205
-
206
- const b = this._currentBucket;
207
- b.count++;
208
- if (isError) {
209
- b.errors++;
210
- }
211
-
212
- b.durationSum += duration;
213
- if (b.durationMin < 0 || duration < b.durationMin) {
214
- b.durationMin = duration;
215
- }
216
- if (b.durationMax < 0 || duration > b.durationMax) {
217
- b.durationMax = duration;
218
- }
219
-
220
- if (b.durations.length < this.config.percentilePrecision) {
221
- b.durations.push(duration);
222
- }
223
- }
224
-
225
- /**
226
- * Get system CPU times.
227
- * @private
228
- * @returns {{user: number, nice: number, sys: number, idle: number, irq: number, total: number}} System CPU times.
229
- */
230
- _getSystemCpuTimes() {
231
- const cpus = os.cpus();
232
- let user = 0;
233
- let nice = 0;
234
- let sys = 0;
235
- let idle = 0;
236
- let irq = 0;
237
-
238
- for (const cpu of cpus) {
239
- user += cpu.times.user;
240
- nice += cpu.times.nice;
241
- sys += cpu.times.sys;
242
- idle += cpu.times.idle;
243
- irq += cpu.times.irq;
244
- }
245
-
246
- const total = user + nice + sys + idle + irq;
247
- return {user, nice, sys, idle, irq, total};
248
- }
249
-
250
- /**
251
- * Calculate CPU usage percentage since last call.
252
- * @private
253
- * @returns {number} CPU usage percentage (0-100).
254
- */
255
- _calculateCpuUsage() {
256
- const current = this._getSystemCpuTimes();
257
- const last = this._lastCpuTimes || {user: 0, nice: 0, sys: 0, idle: 0, irq: 0, total: 0};
258
-
259
- const deltaTotal = current.total - last.total;
260
- const deltaIdle = current.idle - last.idle;
261
-
262
- this._lastCpuTimes = current;
263
-
264
- if (deltaTotal <= 0) return 0;
265
-
266
- const percent = ((deltaTotal - deltaIdle) / deltaTotal) * 100;
267
- return Math.min(100, Math.max(0, percent));
268
- }
269
-
270
- /**
271
- * Rotate the current bucket into history and start a new one.
272
- * @param {PoolSnapshot[]} [poolSnapshots] - Optional pool snapshots to include.
273
- */
274
- rotateBucket(poolSnapshots = []) {
275
- const b = this._currentBucket;
276
- const memUsage = process.memoryUsage();
277
- const systemMemoryUsed = os.totalmem() - os.freemem();
278
- const cpuUsage = process.cpuUsage();
279
- const cpu = this._calculateCpuUsage();
280
-
281
- // Update lifetime extremes
282
- const reqPerSec = b.count / (this.config.intervalMs / 1000);
283
- this.lifetime.maxRequestsPerSecond = Math.max(this.lifetime.maxRequestsPerSecond, reqPerSec);
284
- this.lifetime.memory.heapUsedMax = Math.max(this.lifetime.memory.heapUsedMax, memUsage.heapUsed);
285
- this.lifetime.memory.heapTotalMax = Math.max(this.lifetime.memory.heapTotalMax, memUsage.heapTotal);
286
- this.lifetime.memory.rssMax = Math.max(this.lifetime.memory.rssMax, systemMemoryUsed);
287
- this.lifetime.memory.externalMax = Math.max(this.lifetime.memory.externalMax, memUsage.external);
288
- this.lifetime.cpu.max = Math.max(this.lifetime.cpu.max, cpu);
289
- this.lifetime.cpu.userMax = Math.max(this.lifetime.cpu.userMax, cpuUsage.user);
290
- this.lifetime.cpu.systemMax = Math.max(this.lifetime.cpu.systemMax, cpuUsage.system);
291
-
292
- let p95 = 0;
293
- let p99 = 0;
294
- if (b.durations.length > 0) {
295
- const sorted = [...b.durations].sort((x, y) => x - y);
296
- const p95Idx = Math.floor(sorted.length * 0.95);
297
- const p99Idx = Math.floor(sorted.length * 0.99);
298
- const lastIdx = sorted.length - 1;
299
-
300
- p95 = sorted[p95Idx] ?? sorted[lastIdx] ?? 0;
301
- p99 = sorted[p99Idx] ?? sorted[lastIdx] ?? 0;
302
- }
303
-
304
- /** @type {Bucket} */
305
- const bucket = {
306
- timestamp: Date.now(),
307
- requests: b.count,
308
- errors: b.errors,
309
- durationMin: b.durationMin < 0 ? 0 : b.durationMin,
310
- durationMax: b.durationMax < 0 ? 0 : b.durationMax,
311
- durationAvg: b.count > 0 ? b.durationSum / b.count : 0,
312
- durationP95: p95,
313
- durationP99: p99,
314
- system: {
315
- cpu,
316
- heapUsed: memUsage.heapUsed,
317
- heapTotal: memUsage.heapTotal,
318
- rss: systemMemoryUsed,
319
- external: memUsage.external,
320
- },
321
- pools: poolSnapshots,
322
- };
323
-
324
- this.history.push(bucket);
325
- if (this.history.length > this.config.maxHistoryPoints) {
326
- this.history.shift();
327
- }
328
-
329
- this._resetBucket();
330
- debug('Bucket rotated: %j', bucket);
331
- }
332
-
333
- /**
334
- * Stop the background timer.
335
- */
336
- stop() {
337
- if (this._timer) {
338
- clearInterval(this._timer);
339
- this._timer = undefined;
340
- }
341
- }
342
-
343
- /**
344
- * Get lifetime summary.
345
- * @returns {StatsSummary} Summary.
346
- */
347
- getSummary() {
348
- return {
349
- startTime: this.startTime,
350
- totalRequests: this.lifetime.totalRequests,
351
- totalErrors: this.lifetime.totalErrors,
352
- avgResponseTime: this.lifetime.totalRequests > 0 ? this.lifetime.totalDuration / this.lifetime.totalRequests : 0,
353
- minResponseTime: this.lifetime.minDuration,
354
- maxResponseTime: this.lifetime.maxDuration,
355
- maxRequestsPerSecond: this.lifetime.maxRequestsPerSecond,
356
- maxMemory: this.lifetime.memory,
357
- cpu: this.lifetime.cpu,
358
- };
359
- }
360
-
361
- /**
362
- * Get history buffer.
363
- * @returns {Bucket[]} The history buffer.
364
- */
365
- getHistory() {
366
- return this.history;
367
- }
368
- }