web_plsql 0.18.0 → 1.2.0

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 (74) hide show
  1. package/README.md +29 -9
  2. package/package.json +17 -7
  3. package/src/admin/client/charts.ts +598 -0
  4. package/src/admin/client/main.js +3 -0
  5. package/src/admin/client/tailwind.css +41 -0
  6. package/src/admin/favicon.svg +3 -0
  7. package/src/admin/index.html +529 -0
  8. package/src/admin/js/api.ts +170 -0
  9. package/src/admin/js/app.ts +614 -0
  10. package/src/admin/js/eslint.config.js +74 -0
  11. package/src/admin/js/schemas.ts +215 -0
  12. package/src/admin/js/templates/config.ts +147 -0
  13. package/src/admin/js/templates/errorRow.ts +21 -0
  14. package/src/admin/js/templates/index.ts +4 -0
  15. package/src/admin/js/templates/poolCard.ts +61 -0
  16. package/src/admin/js/templates/traceRow.ts +24 -0
  17. package/src/admin/js/tsconfig.json +24 -0
  18. package/src/admin/js/types.ts +336 -0
  19. package/src/admin/js/ui/components.ts +44 -0
  20. package/src/admin/js/ui/table.ts +173 -0
  21. package/src/admin/js/ui/theme.ts +93 -0
  22. package/src/admin/js/ui/views.ts +427 -0
  23. package/src/admin/js/util/format.ts +46 -0
  24. package/src/admin/js/util/metrics.ts +24 -0
  25. package/src/admin/lib/chart.bundle.css +1 -0
  26. package/src/admin/lib/chart.bundle.js +146 -0
  27. package/src/admin/style.css +1437 -0
  28. package/src/bin/load-test.js +202 -0
  29. package/src/handler/handlerAdmin.js +293 -0
  30. package/src/handler/plsql/cgi.js +1 -1
  31. package/src/handler/plsql/errorPage.js +31 -6
  32. package/src/handler/plsql/handlerPlSql.js +32 -6
  33. package/src/handler/plsql/owaPageStream.js +93 -0
  34. package/src/handler/plsql/parsePage.js +7 -3
  35. package/src/handler/plsql/procedure.js +221 -105
  36. package/src/handler/plsql/procedureNamed.js +34 -49
  37. package/src/handler/plsql/procedureSanitize.js +120 -45
  38. package/src/handler/plsql/procedureVariable.js +4 -2
  39. package/src/handler/plsql/request.js +8 -3
  40. package/src/handler/plsql/sendResponse.js +43 -5
  41. package/src/index.js +0 -1
  42. package/src/server/adminContext.js +23 -0
  43. package/src/server/config.js +1 -0
  44. package/src/server/server.js +130 -9
  45. package/src/types.js +7 -1
  46. package/src/util/cache.js +123 -0
  47. package/src/util/jsonLogger.js +47 -0
  48. package/src/util/shutdown.js +6 -2
  49. package/src/util/statsManager.js +368 -0
  50. package/src/util/trace.js +7 -6
  51. package/src/util/traceManager.js +68 -0
  52. package/src/version.js +1 -1
  53. package/types/admin/js/schemas.d.ts +384 -0
  54. package/types/admin/js/types.d.ts +312 -0
  55. package/types/handler/handlerAdmin.d.ts +79 -0
  56. package/types/handler/plsql/errorPage.d.ts +1 -0
  57. package/types/handler/plsql/handlerPlSql.d.ts +6 -1
  58. package/types/handler/plsql/owaPageStream.d.ts +28 -0
  59. package/types/handler/plsql/procedure.d.ts +4 -1
  60. package/types/handler/plsql/procedureNamed.d.ts +2 -5
  61. package/types/handler/plsql/procedureSanitize.d.ts +2 -5
  62. package/types/handler/plsql/procedureVariable.d.ts +1 -1
  63. package/types/handler/plsql/request.d.ts +3 -1
  64. package/types/handler/plsql/sendResponse.d.ts +1 -1
  65. package/types/index.d.ts +0 -1
  66. package/types/server/adminContext.d.ts +17 -0
  67. package/types/server/server.d.ts +5 -0
  68. package/types/types.d.ts +19 -1
  69. package/types/util/cache.d.ts +69 -0
  70. package/types/util/jsonLogger.d.ts +45 -0
  71. package/types/util/statsManager.d.ts +395 -0
  72. package/types/util/traceManager.d.ts +35 -0
  73. package/src/handler/handlerMetrics.js +0 -68
  74. package/types/handler/handlerMetrics.d.ts +0 -25
@@ -0,0 +1,79 @@
1
+ /**
2
+ * @typedef {import('express').Request} Request
3
+ * @typedef {import('express').Response} Response
4
+ * @typedef {import('express').NextFunction} NextFunction
5
+ */
6
+ /**
7
+ * @typedef {import('../util/statsManager.js').Bucket} Bucket
8
+ */
9
+ /**
10
+ * @typedef {object} StatsSummary
11
+ * @property {Date} startTime - Server start time.
12
+ * @property {number} totalRequests - Total requests handled.
13
+ * @property {number} totalErrors - Total errors encountered.
14
+ * @property {number} avgResponseTime - Lifetime average response time.
15
+ * @property {number} minResponseTime - Lifetime minimum response time.
16
+ * @property {number} maxResponseTime - Lifetime maximum response time.
17
+ * @property {number} maxRequestsPerSecond - Lifetime maximum requests per second.
18
+ * @property {object} maxMemory - Lifetime memory extremes.
19
+ * @property {number} maxMemory.heapUsedMax - Maximum heap used.
20
+ * @property {number} maxMemory.heapTotalMax - Maximum heap total.
21
+ * @property {number} maxMemory.rssMax - Maximum RSS.
22
+ * @property {number} maxMemory.externalMax - Maximum external memory.
23
+ * @property {object} cpu - Lifetime CPU extremes.
24
+ * @property {number} cpu.max - Max CPU.
25
+ * @property {number} cpu.userMax - Max user CPU.
26
+ * @property {number} cpu.systemMax - Max system CPU.
27
+ */
28
+ export const handlerAdmin: import("express-serve-static-core").Router;
29
+ export type Request = import("express").Request;
30
+ export type Response = import("express").Response;
31
+ export type NextFunction = import("express").NextFunction;
32
+ export type Bucket = import("../util/statsManager.js").Bucket;
33
+ export type StatsSummary = {
34
+ /**
35
+ * - Server start time.
36
+ */
37
+ startTime: Date;
38
+ /**
39
+ * - Total requests handled.
40
+ */
41
+ totalRequests: number;
42
+ /**
43
+ * - Total errors encountered.
44
+ */
45
+ totalErrors: number;
46
+ /**
47
+ * - Lifetime average response time.
48
+ */
49
+ avgResponseTime: number;
50
+ /**
51
+ * - Lifetime minimum response time.
52
+ */
53
+ minResponseTime: number;
54
+ /**
55
+ * - Lifetime maximum response time.
56
+ */
57
+ maxResponseTime: number;
58
+ /**
59
+ * - Lifetime maximum requests per second.
60
+ */
61
+ maxRequestsPerSecond: number;
62
+ /**
63
+ * - Lifetime memory extremes.
64
+ */
65
+ maxMemory: {
66
+ heapUsedMax: number;
67
+ heapTotalMax: number;
68
+ rssMax: number;
69
+ externalMax: number;
70
+ };
71
+ /**
72
+ * - Lifetime CPU extremes.
73
+ */
74
+ cpu: {
75
+ max: number;
76
+ userMax: number;
77
+ systemMax: number;
78
+ };
79
+ };
@@ -8,3 +8,4 @@ export type outputType = {
8
8
  html: string;
9
9
  text: string;
10
10
  };
11
+ export type messageType = import("../../util/trace.js").messageType;
@@ -1,4 +1,4 @@
1
- export function handlerWebPlSql(connectionPool: Pool, config: configPlSqlHandlerType): RequestHandler;
1
+ export function handlerWebPlSql(connectionPool: Pool, config: configPlSqlHandlerType): WebPlSqlRequestHandler;
2
2
  export type RequestHandler = import("express").RequestHandler;
3
3
  export type Request = import("express").Request;
4
4
  export type Response = import("express").Response;
@@ -6,3 +6,8 @@ export type NextFunction = import("express").NextFunction;
6
6
  export type Pool = import("oracledb").Pool;
7
7
  export type environmentType = import("../../types.js").environmentType;
8
8
  export type configPlSqlHandlerType = import("../../types.js").configPlSqlHandlerType;
9
+ export type WebPlSqlRequestHandler = import("express").RequestHandler & {
10
+ procedureNameCache: Cache<string>;
11
+ argumentCache: Cache<import("./procedureNamed.js").argsType>;
12
+ };
13
+ import { Cache } from '../../util/cache.js';
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @typedef {import('oracledb').Connection} Connection
3
+ * @typedef {import('../../types.js').BindParameterConfig} BindParameterConfig
4
+ */
5
+ export class OWAPageStream extends Readable {
6
+ /**
7
+ * @param {Connection} databaseConnection - The database connection.
8
+ */
9
+ constructor(databaseConnection: Connection);
10
+ databaseConnection: oracledb.Connection;
11
+ chunkSize: number;
12
+ isDone: boolean;
13
+ /**
14
+ * Fetch a chunk of the page from the database.
15
+ * @returns {Promise<string[]>} The array of lines fetched.
16
+ */
17
+ fetchChunk(): Promise<string[]>;
18
+ /**
19
+ * Add initial body content to the stream.
20
+ * @param {string} content - The initial content to prepend.
21
+ * @returns {void}
22
+ */
23
+ addBody(content: string): void;
24
+ }
25
+ export type Connection = import("oracledb").Connection;
26
+ export type BindParameterConfig = import("../../types.js").BindParameterConfig;
27
+ import { Readable } from 'node:stream';
28
+ import oracledb from 'oracledb';
@@ -1,4 +1,5 @@
1
- export function invokeProcedure(req: Request, res: Response, argObj: argObjType, cgiObj: environmentType, filesToUpload: fileUploadType[], options: configPlSqlHandlerType, databaseConnection: Connection): Promise<void>;
1
+ export function invokeProcedure(req: Request, res: Response, argObj: argObjType, cgiObj: environmentType, filesToUpload: fileUploadType[], options: configPlSqlHandlerType, databaseConnection: Connection, procedureNameCache: ProcedureNameCache, argumentCache: ArgumentCache): Promise<void>;
2
+ export type TraceEntry = import("../../admin/js/types.js").TraceEntry;
2
3
  export type Request = import("express").Request;
3
4
  export type Response = import("express").Response;
4
5
  export type Connection = import("oracledb").Connection;
@@ -8,3 +9,5 @@ export type fileUploadType = import("../../types.js").fileUploadType;
8
9
  export type environmentType = import("../../types.js").environmentType;
9
10
  export type configPlSqlHandlerType = import("../../types.js").configPlSqlHandlerType;
10
11
  export type BindParameterConfig = import("../../types.js").BindParameterConfig;
12
+ export type ProcedureNameCache = import("../../util/cache.js").Cache<string>;
13
+ export type ArgumentCache = import("../../util/cache.js").Cache<import("./procedureNamed.js").argsType>;
@@ -1,5 +1,5 @@
1
1
  export function getBinding(argName: string, argValue: unknown, argType: string): BindParameter;
2
- export function getProcedureNamed(req: Request, procName: string, argObj: argObjType, databaseConnection: Connection): Promise<{
2
+ export function getProcedureNamed(req: Request, procName: string, argObj: argObjType, databaseConnection: Connection, argumentCache: ArgumentCache): Promise<{
3
3
  sql: string;
4
4
  bind: BindParameterConfig;
5
5
  }>;
@@ -11,7 +11,4 @@ export type argObjType = import("../../types.js").argObjType;
11
11
  export type BindParameterConfig = import("../../types.js").BindParameterConfig;
12
12
  export type BindParameter = import("../../types.js").BindParameter;
13
13
  export type argsType = Record<string, string>;
14
- export type cacheEntryType = {
15
- hitCount: number;
16
- args: argsType;
17
- };
14
+ export type ArgumentCache = import("../../util/cache.js").Cache<argsType>;
@@ -1,4 +1,4 @@
1
- export function sanitizeProcName(procName: string, databaseConnection: Connection, options: configPlSqlHandlerType): Promise<string>;
1
+ export function sanitizeProcName(procName: string, databaseConnection: Connection, options: configPlSqlHandlerType, procedureNameCache: ProcedureNameCache): Promise<string>;
2
2
  export type Request = import("express").Request;
3
3
  export type Response = import("express").Response;
4
4
  export type Connection = import("oracledb").Connection;
@@ -8,7 +8,4 @@ export type fileUploadType = import("../../types.js").fileUploadType;
8
8
  export type environmentType = import("../../types.js").environmentType;
9
9
  export type configPlSqlHandlerType = import("../../types.js").configPlSqlHandlerType;
10
10
  export type BindParameterConfig = import("../../types.js").BindParameterConfig;
11
- export type cacheEntryType = {
12
- hitCount: number;
13
- valid: boolean;
14
- };
11
+ export type ProcedureNameCache = import("../../util/cache.js").Cache<string>;
@@ -1,4 +1,4 @@
1
- export function getProcedureVariable(req: Request, procName: string, argObj: argObjType): {
1
+ export function getProcedureVariable(_req: Request, procName: string, argObj: argObjType): {
2
2
  sql: string;
3
3
  bind: BindParameterConfig;
4
4
  };
@@ -1,7 +1,9 @@
1
- export function processRequest(req: Request, res: Response, options: configPlSqlHandlerType, connectionPool: Pool): Promise<void>;
1
+ export function processRequest(req: Request, res: Response, options: configPlSqlHandlerType, connectionPool: Pool, procedureNameCache: ProcedureNameCache, argumentCache: ArgumentCache): Promise<void>;
2
2
  export type Request = import("express").Request;
3
3
  export type Response = import("express").Response;
4
4
  export type Pool = import("oracledb").Pool;
5
5
  export type Connection = import("oracledb").Connection;
6
6
  export type argObjType = import("../../types.js").argObjType;
7
7
  export type configPlSqlHandlerType = import("../../types.js").configPlSqlHandlerType;
8
+ export type ProcedureNameCache = import("../../util/cache.js").Cache<string>;
9
+ export type ArgumentCache = import("../../util/cache.js").Cache<import("./procedureNamed.js").argsType>;
@@ -1,4 +1,4 @@
1
- export function sendResponse(req: Request, res: Response, page: pageType): Promise<void>;
1
+ export function sendResponse(_req: Request, res: Response, page: pageType): Promise<void>;
2
2
  export type Request = import("express").Request;
3
3
  export type Response = import("express").Response;
4
4
  export type CookieOptions = import("express").CookieOptions;
package/types/index.d.ts CHANGED
@@ -6,6 +6,5 @@ export * from "./util/file.js";
6
6
  export { handlerWebPlSql } from "./handler/plsql/handlerPlSql.js";
7
7
  export { handlerLogger } from "./handler/handlerLogger.js";
8
8
  export { handlerUpload } from "./handler/handlerUpload.js";
9
- export { handlerMetrics } from "./handler/handlerMetrics.js";
10
9
  import oracledb from 'oracledb';
11
10
  export { createServer, startServer, loadConfig, startServerConfig } from "./server/server.js";
@@ -0,0 +1,17 @@
1
+ export namespace AdminContext {
2
+ let startTime: Date;
3
+ let config: configType | null;
4
+ let pools: Pool[];
5
+ let caches: Array<{
6
+ poolName: string;
7
+ procedureNameCache: Cache;
8
+ argumentCache: Cache;
9
+ }>;
10
+ let paused: boolean;
11
+ let statsManager: StatsManager;
12
+ }
13
+ export type Pool = import("oracledb").Pool;
14
+ export type configType = import("../types.js").configType;
15
+ export type argsType = import("../handler/plsql/procedureNamed.js").argsType;
16
+ export type Cache = import("../util/cache.js").Cache<unknown>;
17
+ import { StatsManager } from '../util/statsManager.js';
@@ -10,6 +10,11 @@ export type NextFunction = import("express").NextFunction;
10
10
  export type Pool = import("oracledb").Pool;
11
11
  export type environmentType = import("../types.js").environmentType;
12
12
  export type configType = import("../types.js").configType;
13
+ export type argsType = import("../handler/plsql/procedureNamed.js").argsType;
14
+ export type ExtendedRequestHandler = import("express").RequestHandler & {
15
+ procedureNameCache: import("../util/cache.js").Cache<string>;
16
+ argumentCache: import("../util/cache.js").Cache<argsType>;
17
+ };
13
18
  /**
14
19
  * - Web server interface.
15
20
  */
package/types/types.d.ts CHANGED
@@ -92,6 +92,9 @@ export const z$configPlSqlType: z.ZodObject<{
92
92
  * @property {configPlSqlType[]} routePlSql - The PL/SQL routes.
93
93
  * @property {number} [uploadFileSizeLimit] - Maximum size of each uploaded file in bytes or no limit if omitted.
94
94
  * @property {string} loggerFilename - name of the request logger filename or '' if not required.
95
+ * @property {string} [adminRoute] - Optional route for the admin console (defaults to /admin).
96
+ * @property {string} [adminUser] - Optional username for admin console basic auth.
97
+ * @property {string} [adminPassword] - Optional password for admin console basic auth.
95
98
  */
96
99
  export const z$configType: z.ZodObject<{
97
100
  port: z.ZodNumber;
@@ -118,6 +121,9 @@ export const z$configType: z.ZodObject<{
118
121
  }, z.core.$strict>>;
119
122
  uploadFileSizeLimit: z.ZodOptional<z.ZodNumber>;
120
123
  loggerFilename: z.ZodString;
124
+ adminRoute: z.ZodOptional<z.ZodString>;
125
+ adminUser: z.ZodOptional<z.ZodString>;
126
+ adminPassword: z.ZodOptional<z.ZodString>;
121
127
  }, z.core.$strict>;
122
128
  export type BindParameter = import("oracledb").BindParameter;
123
129
  export type Connection = import("oracledb").Connection;
@@ -217,6 +223,18 @@ export type configType = {
217
223
  * - name of the request logger filename or '' if not required.
218
224
  */
219
225
  loggerFilename: string;
226
+ /**
227
+ * - Optional route for the admin console (defaults to /admin).
228
+ */
229
+ adminRoute?: string;
230
+ /**
231
+ * - Optional username for admin console basic auth.
232
+ */
233
+ adminUser?: string;
234
+ /**
235
+ * - Optional password for admin console basic auth.
236
+ */
237
+ adminPassword?: string;
220
238
  };
221
239
  /**
222
240
  * Environment variables as string key-value pairs
@@ -284,7 +302,7 @@ export type pageType = {
284
302
  /**
285
303
  * - The body of the page.
286
304
  */
287
- body: string;
305
+ body: string | import("node:stream").Readable;
288
306
  /**
289
307
  * - The head of the page.
290
308
  */
@@ -0,0 +1,69 @@
1
+ /**
2
+ * @template T
3
+ * @typedef {{hitCount: number, value: T}} cacheEntryType
4
+ */
5
+ /**
6
+ * Generic Cache class with LFU (Least Frequently Used) eviction policy.
7
+ * @template T
8
+ */
9
+ export class Cache<T> {
10
+ /**
11
+ * @param {number} maxSize - Maximum number of entries in the cache.
12
+ */
13
+ constructor(maxSize?: number);
14
+ /** @type {Map<string, cacheEntryType<T>>} */
15
+ cache: Map<string, cacheEntryType<T>>;
16
+ maxSize: number;
17
+ hits: number;
18
+ misses: number;
19
+ /**
20
+ * Get an entry from the cache.
21
+ * @param {string} key - The key.
22
+ * @returns {T | undefined} - The value or undefined if not found.
23
+ */
24
+ get(key: string): T | undefined;
25
+ /**
26
+ * Set an entry in the cache.
27
+ * @param {string} key - The key.
28
+ * @param {T} value - The value.
29
+ */
30
+ set(key: string, value: T): void;
31
+ /**
32
+ * Delete an entry from the cache.
33
+ * @param {string} key - The key.
34
+ */
35
+ delete(key: string): void;
36
+ /**
37
+ * Clear the cache.
38
+ */
39
+ clear(): void;
40
+ /**
41
+ * Prune the cache by removing the least frequently used entries.
42
+ * Removes 10% of the cache size.
43
+ */
44
+ prune(): void;
45
+ /**
46
+ * Get the size of the cache.
47
+ * @returns {number} - The size.
48
+ */
49
+ get size(): number;
50
+ /**
51
+ * Get all keys in the cache.
52
+ * @returns {string[]} - The keys.
53
+ */
54
+ keys(): string[];
55
+ /**
56
+ * Get cache statistics.
57
+ * @returns {{size: number, maxSize: number, hits: number, misses: number}} - The statistics.
58
+ */
59
+ getStats(): {
60
+ size: number;
61
+ maxSize: number;
62
+ hits: number;
63
+ misses: number;
64
+ };
65
+ }
66
+ export type cacheEntryType<T> = {
67
+ hitCount: number;
68
+ value: T;
69
+ };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @typedef {object} LogEntry
3
+ * @property {string} timestamp - ISO string
4
+ * @property {'error'|'info'|'warning'} type - Log level
5
+ * @property {string} message - Error message
6
+ * @property {object} [req] - Request details
7
+ * @property {object} [details] - Additional details (stack, sql, etc)
8
+ */
9
+ export class JsonLogger {
10
+ constructor(filename?: string);
11
+ stream: rotatingFileStream.RotatingFileStream;
12
+ /**
13
+ * Log an entry as NDJSON.
14
+ * @param {LogEntry} entry - The entry to log.
15
+ */
16
+ log(entry: LogEntry): void;
17
+ /**
18
+ * Close the stream.
19
+ */
20
+ close(): void;
21
+ }
22
+ export const jsonLogger: JsonLogger;
23
+ export type LogEntry = {
24
+ /**
25
+ * - ISO string
26
+ */
27
+ timestamp: string;
28
+ /**
29
+ * - Log level
30
+ */
31
+ type: "error" | "info" | "warning";
32
+ /**
33
+ * - Error message
34
+ */
35
+ message: string;
36
+ /**
37
+ * - Request details
38
+ */
39
+ req?: object;
40
+ /**
41
+ * - Additional details (stack, sql, etc)
42
+ */
43
+ details?: object;
44
+ };
45
+ import * as rotatingFileStream from 'rotating-file-stream';