{"version":3,"file":"index.mjs","names":["z","z","createPool","debug","oracledb.createPool","debug","debug","fs","debug","debug","debug","debug","debug","debug","debug","debug","debug","URL","debug","debug","z","process"],"sources":["../src/common/configStaticSchema.ts","../src/common/procedureTraceEntry.ts","../src/common/logEntrySchema.ts","../src/backend/types.ts","../src/backend/util/oracledb-mock.ts","../src/backend/util/oracledb-provider.ts","../src/backend/version.ts","../src/backend/server/printBanner.ts","../src/backend/server/server.ts","../src/backend/util/shutdown.ts","../src/common/constants.ts","../src/backend/util/statsManager.ts","../src/backend/server/adminContext.ts","../src/backend/util/file.ts","../src/backend/util/html.ts","../src/backend/util/trace.ts","../src/backend/util/errorToString.ts","../src/backend/handler/plsql/upload.ts","../src/backend/handler/plsql/procedureVariable.ts","../src/backend/handler/plsql/requestError.ts","../src/backend/util/util.ts","../src/backend/handler/plsql/procedureNamed.ts","../src/backend/handler/plsql/parsePage.ts","../src/backend/handler/plsql/sendResponse.ts","../src/backend/handler/plsql/procedureError.ts","../src/backend/util/cache.ts","../src/backend/handler/plsql/procedureSanitize.ts","../src/backend/handler/plsql/owaPageStream.ts","../src/backend/util/traceManager.ts","../src/backend/handler/plsql/procedure.ts","../src/backend/handler/plsql/cgi.ts","../src/backend/util/type.ts","../src/backend/handler/plsql/request.ts","../src/backend/util/jsonLogger.ts","../src/backend/handler/plsql/errorPage.ts","../src/backend/handler/plsql/handlerPlSql.ts","../src/backend/handler/handlerLogger.ts","../src/backend/handler/handlerUpload.ts","../src/backend/handler/handlerAdmin.ts","../src/backend/handler/handlerAdminConsole.ts","../src/backend/handler/handlerSpaFallback.ts","../src/backend/util/printError.ts","../src/backend/util/logError.ts"],"sourcesContent":["import z from 'zod';\n\n/**\n * Configuration for serving static files\n */\nexport const configStaticSchema = z.strictObject({\n\t/** URL route prefix for static assets */\n\troute: z.string(),\n\t/** Local filesystem path to the static assets directory */\n\tdirectoryPath: z.string(),\n\t/**\n\t * Enable SPA fallback mode.\n\t * When true, serves index.html for unmatched routes (for React Router, Vue Router, etc.)\n\t * Requires: Application uses HTML5 History Mode routing\n\t * Default: false\n\t */\n\tspaFallback: z.boolean().optional(),\n});\nexport type configStaticType = z.infer<typeof configStaticSchema>;","import {z} from 'zod';\n\nexport const procedureTraceEntrySchema = z.strictObject({\n\tid: z.string(),\n\ttimestamp: z.string(),\n\tsource: z.string(),\n\turl: z.string(),\n\tmethod: z.string(),\n\tstatus: z.string(),\n\tduration: z.number(),\n\tprocedure: z.string().optional(),\n\tparameters: z.union([z.record(z.string(), z.unknown()), z.array(z.unknown())]).optional(),\n\tuploads: z\n\t\t.array(\n\t\t\tz.strictObject({\n\t\t\t\toriginalname: z.string(),\n\t\t\t\tmimetype: z.string(),\n\t\t\t\tsize: z.number(),\n\t\t\t}),\n\t\t)\n\t\t.optional(),\n\tdownloads: z\n\t\t.strictObject({\n\t\t\tfileType: z.string(),\n\t\t\tfileSize: z.number(),\n\t\t})\n\t\t.optional(),\n\thtml: z.string().optional(),\n\tcookies: z.record(z.string(), z.string()).optional(),\n\theaders: z.record(z.string(), z.string()).optional(),\n\tcgi: z.record(z.string(), z.string()).optional(),\n\terror: z.string().optional(),\n});\n\nexport type procedureTraceEntry = z.infer<typeof procedureTraceEntrySchema>;","import {z} from 'zod';\n\n/**\n * Error log entry schema.\n */\nconst logEntryTypeSchema = z.union([z.literal('error'), z.literal('info'), z.literal('warning')]);\nexport const logEntrySchema = z.strictObject({\n\ttimestamp: z.string(),\n\ttype: logEntryTypeSchema,\n\tmessage: z.string(),\n\treq: z\n\t\t.strictObject({\n\t\t\tmethod: z.string().optional(),\n\t\t\turl: z.string().optional(),\n\t\t\tip: z.string().optional(),\n\t\t\tuserAgent: z.string().optional(),\n\t\t})\n\t\t.optional(),\n\tdetails: z\n\t\t.strictObject({\n\t\t\tfullMessage: z.string().optional(),\n\t\t\tsql: z.string().optional(),\n\t\t\tbind: z.unknown().optional(),\n\t\t\tenvironment: z.record(z.string(), z.string()).optional(),\n\t\t})\n\t\t.optional(),\n});\nexport type logEntryType = z.infer<typeof logEntrySchema>;","import z from 'zod';\nimport {configStaticSchema} from '../common/configStaticSchema.ts';\nimport type {Connection, Pool} from 'oracledb';\nimport type {CookieOptions, Express, Request} from 'express';\nimport type {Readable} from 'node:stream';\nimport type {Cache} from './util/cache.ts';\n\nexport {procedureTraceEntrySchema, type procedureTraceEntry} from '../common/procedureTraceEntry.ts';\nexport {logEntrySchema, type logEntryType} from '../common/logEntrySchema.ts';\n\n/**\n * Defines the style of error reporting\n * 'basic': standard error messages\n * 'debug': detailed error messages including database context\n */\nconst z$errorStyleType = z.enum(['basic', 'debug']);\n\n/**\n * Custom callback signature for manual transaction handling\n */\ntype transactionCallbackType = (connection: Connection, procedure: string) => void | Promise<void>;\n\n/**\n * Defines how transactions are handled after procedure execution\n * 'commit': automatically commit\n * 'rollback': automatically rollback\n * callback: custom function for manual handling\n */\nconst transactionModeSchema = z.union([\n\tz.custom<transactionCallbackType>((val) => typeof val === 'function', {\n\t\tmessage: 'Invalid transaction callback',\n\t}),\n\tz.literal('commit'),\n\tz.literal('rollback'),\n\tz.undefined(),\n\tz.null(),\n]);\nexport type transactionModeType = z.infer<typeof transactionModeSchema>;\n\n/**\n * Basic authentication callback signature.\n * Returns the identity string on success, or null on failure.\n * @public\n */\nexport type BasicAuthCallback = (credentials: {username: string; password?: string | undefined}, connectionPool: Pool) => Promise<string | null>;\n\n/**\n * Custom authentication callback signature.\n * Returns the identity string on success, or null on failure.\n * @public\n */\nexport type CustomAuthCallback = (req: Request, connectionPool: Pool) => Promise<string | null>;\n\n/**\n * Authentication configuration for a PL/SQL route\n */\nconst z$authSchema = z.union([\n\tz.strictObject({\n\t\t/** Authentication type */\n\t\ttype: z.literal('basic'),\n\t\t/** Callback function to validate credentials */\n\t\tcallback: z.custom<BasicAuthCallback>((val) => typeof val === 'function', {\n\t\t\tmessage: 'Invalid auth callback',\n\t\t}),\n\t\t/** Authentication realm */\n\t\trealm: z.string().optional(),\n\t}),\n\tz.strictObject({\n\t\t/** Authentication type */\n\t\ttype: z.literal('custom'),\n\t\t/** Callback function to validate request */\n\t\tcallback: z.custom<CustomAuthCallback>((val) => typeof val === 'function', {\n\t\t\tmessage: 'Invalid auth callback',\n\t\t}),\n\t}),\n]);\n\n/**\n * PL/SQL handler behavior configuration\n */\nexport const z$configPlSqlHandlerType = z.strictObject({\n\t/** Default procedure to execute if none specified */\n\tdefaultPage: z.string(),\n\t/** Virtual path alias for procedures */\n\tpathAlias: z.string().optional(),\n\t/** Procedure name associated with the path alias */\n\tpathAliasProcedure: z.string().optional(),\n\t/** Database table used for file uploads/downloads */\n\tdocumentTable: z.string(),\n\t/** List of pattern/procedure names excluded from execution */\n\texclusionList: z.array(z.string()).optional(),\n\t/** PL/SQL function called to validate requests */\n\trequestValidationFunction: z.string().optional(),\n\t/** Post-execution transaction behavior */\n\ttransactionMode: transactionModeSchema.optional(),\n\t/** Error reporting style */\n\terrorStyle: z$errorStyleType,\n\t/** Static CGI environment variables to be passed to the session */\n\tcgi: z.record(z.string(), z.string()).optional(),\n\t/** Authentication settings */\n\tauth: z$authSchema.optional(),\n});\nexport type configPlSqlHandlerType = z.infer<typeof z$configPlSqlHandlerType>;\n\n/**\n * Database connection configuration for a PL/SQL route\n */\nconst z$configPlSqlConfigType = z.strictObject({\n\t/** URL route prefix for this database connection */\n\troute: z.string(),\n\t/** Database username */\n\tuser: z.string(),\n\t/** Database password */\n\tpassword: z.string(),\n\t/** Oracle connection string (TNS or EZConnect) */\n\tconnectString: z.string(),\n});\ntype configPlSqlConfigType = z.infer<typeof z$configPlSqlConfigType>;\n\n/**\n * Complete PL/SQL route configuration combining handler and connection settings\n */\nexport type configPlSqlType = configPlSqlHandlerType & configPlSqlConfigType;\nconst z$configPlSqlType = z.strictObject({\n\t...z$configPlSqlHandlerType.shape,\n\t...z$configPlSqlConfigType.shape,\n});\n\n/**\n * Root application configuration\n */\nexport const z$configType = z.strictObject({\n\t/** Server listening port */\n\tport: z.number(),\n\t/** Array of static file routes */\n\trouteStatic: z.array(configStaticSchema),\n\t/** Array of PL/SQL routes */\n\troutePlSql: z.array(z$configPlSqlType),\n\t/** Maximum allowed size for file uploads (bytes) */\n\tuploadFileSizeLimit: z.number().optional(),\n\t/** Path to the log file */\n\tloggerFilename: z.string(),\n\t/** URL route prefix for the admin console */\n\tadminRoute: z.string().optional(),\n\t/** Username for admin console authentication */\n\tadminUser: z.string().optional(),\n\t/** Password for admin console authentication */\n\tadminPassword: z.string().optional(),\n\t/** Developer mode (skips frontend build check, enables CORS) */\n\tdevMode: z.boolean().optional(),\n\t/** Callback function to setup custom Express extensions */\n\tsetupExtensions: z\n\t\t.custom<(app: Express, pools: Pool[]) => void | Promise<void>>((val) => typeof val === 'function', {\n\t\t\tmessage: 'Invalid setupExtensions callback',\n\t\t})\n\t\t.optional(),\n\toracle: z\n\t\t.strictObject({\n\t\t\tpoolMin: z.int().min(10).default(10),\n\t\t\tpoolMax: z.int().min(10).default(100),\n\t\t\tpoolIncrement: z.int().min(1).default(10),\n\t\t})\n\t\t.optional()\n\t\t.default({\n\t\t\tpoolMin: 10,\n\t\t\tpoolMax: 100,\n\t\t\tpoolIncrement: 10,\n\t\t}),\n});\nexport type configInputType = z.input<typeof z$configType>;\nexport type configType = z.output<typeof z$configType>;\n\n/**\n * Environment variables as string key-value pairs\n */\nexport type environmentType = Record<string, string>;\n\n/**\n * HTTP arguments mapping with support for multi-value parameters\n */\nexport type argObjType = Record<string, string | string[]>;\n\n/**\n * Mapping of PL/SQL procedure argument names to their database types\n */\nexport type argsType = Record<string, string>;\n\n/**\n * Metadata for uploaded files\n */\nexport type fileUploadType = {\n\t/** HTML form field name */\n\tfieldname: string;\n\t/** Original filename as uploaded by the client */\n\toriginalname: string;\n\t/** Content encoding */\n\tencoding: string;\n\t/** MIME type */\n\tmimetype: string;\n\t/** Local temporary filename */\n\tfilename: string;\n\t/** Absolute path to the temporary file */\n\tpath: string;\n\t/** File size in bytes */\n\tsize: number;\n};\n\n/**\n * HTTP cookie definition\n */\nexport type cookieType = {\n\t/** Cookie name */\n\tname: string;\n\t/** Cookie value */\n\tvalue: string;\n\t/** Express cookie options (domain, path, expires, etc.) */\n\toptions: CookieOptions;\n};\n\n/**\n * Internal representation of a generated web page or file response\n */\nexport type pageType = {\n\t/** Response body content as string or stream */\n\tbody: string | Readable;\n\t/** HTTP response headers and status */\n\thead: {\n\t\t/** Array of cookies to be set */\n\t\tcookies: cookieType[];\n\t\t/** Content-Type header value */\n\t\tcontentType?: string;\n\t\t/** Content-Length header value */\n\t\tcontentLength?: number;\n\t\t/** HTTP status code */\n\t\tstatusCode?: number;\n\t\t/** HTTP status reason phrase */\n\t\tstatusDescription?: string;\n\t\t/** Location header for redirects */\n\t\tredirectLocation?: string;\n\t\t/** Additional custom HTTP headers */\n\t\totherHeaders: Record<string, string>;\n\t\t/** Server header value */\n\t\tserver?: string;\n\t};\n\t/** Metadata for file downloads if applicable */\n\tfile: {\n\t\t/** MIME type of the file */\n\t\tfileType: string | null;\n\t\t/** Size of the file in bytes */\n\t\tfileSize: number | null;\n\t\t/** File content as stream or buffer */\n\t\tfileBlob: Readable | Buffer | null;\n\t};\n};\n\nexport type ProcedureNameCache = Cache<string>;\nexport type ArgumentCache = Cache<argsType>;","import type {Connection, Pool, Lob, Result, BindParameters, ExecuteOptions, DbType} from 'oracledb';\n\n// Mock implementation\nexport type ExecuteCallback = (sql: string, binds?: BindParameters) => Promise<Result<unknown>> | Result<unknown>;\nlet executeCallback: ExecuteCallback | null = null;\n\n/**\n * Test utility: Set the callback for execute calls.\n * @param cb - The callback\n */\nexport const setExecuteCallback = (cb: ExecuteCallback | null) => {\n\texecuteCallback = cb;\n};\n\n/**\n * Mock LOB class.\n */\nclass MockLob {\n\ttype: DbType | number;\n\tconstructor(type: DbType | number) {\n\t\tthis.type = type;\n\t}\n\t// oxlint-disable-next-line typescript/no-empty-function\n\tdestroy(): void {}\n}\n\n/**\n * Mock Connection class.\n */\nclass MockConnection {\n\tasync execute<T = unknown>(sql: string, bindParams?: BindParameters, _options?: ExecuteOptions): Promise<Result<T>> {\n\t\tif (executeCallback) {\n\t\t\treturn (await executeCallback(sql, bindParams)) as Result<T>;\n\t\t}\n\t\treturn {rows: []} as Result<T>;\n\t}\n\n\t// oxlint-disable-next-line typescript/require-await\n\tasync createLob(type: DbType | number): Promise<Lob> {\n\t\treturn new MockLob(type) as unknown as Lob;\n\t}\n\n\tasync commit(): Promise<void> {\n\t\t// empty\n\t}\n\tasync rollback(): Promise<void> {\n\t\t// empty\n\t}\n\tasync release(): Promise<void> {\n\t\t// empty\n\t}\n}\n\n/**\n * Mock Pool class.\n */\nclass MockPool {\n\tconnectionsOpen = 0;\n\tconnectionsInUse = 0;\n\n\t// oxlint-disable-next-line typescript/require-await\n\tasync getConnection(): Promise<Connection> {\n\t\tthis.connectionsInUse++;\n\t\tthis.connectionsOpen = Math.max(this.connectionsOpen, this.connectionsInUse);\n\t\treturn new MockConnection() as unknown as Connection;\n\t}\n\n\t// oxlint-disable-next-line typescript/require-await\n\tasync close(_drainTime?: number): Promise<void> {\n\t\tthis.connectionsOpen = 0;\n\t\tthis.connectionsInUse = 0;\n\t}\n}\n\n/**\n * Mock createPool function.\n * @param _config - The configuration\n * @returns The pool\n */\n// oxlint-disable-next-line typescript/require-await\nexport async function createPool(_config: unknown): Promise<Pool> {\n\treturn new MockPool() as unknown as Pool;\n}\n\n// Default export mimics oracledb module structure for test mocking\nexport default {\n\t// Constants will be merged from actual oracledb by vi.importActual in test_setup.ts\n\tcreatePool,\n\tsetExecuteCallback,\n};","import oracledb from 'oracledb';\n\nconst USE_MOCK = process.env.MOCK_ORACLE === 'true';\n\n/**\n * Create a database pool.\n * @param config - The pool attributes.\n * @returns The pool.\n */\n// Runtime switch for createPool\nexport async function createPool(config: oracledb.PoolAttributes): Promise<oracledb.Pool> {\n\tif (USE_MOCK) {\n\t\tconst mock = await import('./oracledb-mock.ts');\n\t\treturn mock.createPool(config);\n\t}\n\treturn await oracledb.createPool(config);\n}\n\n// Always export real oracledb constants (they're identical in mock)\nexport const BIND_IN = oracledb.BIND_IN;\nexport const BIND_OUT = oracledb.BIND_OUT;\nexport const BIND_INOUT = oracledb.BIND_INOUT;\nexport const STRING = oracledb.STRING;\nexport const NUMBER = oracledb.NUMBER;\nexport const DATE = oracledb.DATE;\nexport const CURSOR = oracledb.CURSOR;\nexport const BUFFER = oracledb.BUFFER;\nexport const CLOB = oracledb.CLOB;\nexport const BLOB = oracledb.BLOB;\nexport const DB_TYPE_VARCHAR = oracledb.DB_TYPE_VARCHAR;\nexport const DB_TYPE_CLOB = oracledb.DB_TYPE_CLOB;\nexport const DB_TYPE_NUMBER = oracledb.DB_TYPE_NUMBER;\nexport const DB_TYPE_DATE = oracledb.DB_TYPE_DATE;\n\n// Re-export types from real oracledb for convenience\nexport type {Connection, Pool, Lob, Result, BindParameter, BindParameters, ExecuteOptions, DbType} from 'oracledb';\n\n// Export mock-specific utilities\nexport {setExecuteCallback, type ExecuteCallback} from './oracledb-mock.ts';","declare global {\n\tvar __VERSION__: string;\n}\n\nglobalThis.__VERSION__ ??= '**development**';\n\ndeclare const __VERSION__: string;\n\n/**\n * Returns the current library version\n * @returns {string} - Version.\n */\nexport const getVersion = () => __VERSION__;","import chalk from 'chalk';\nimport stringWidth from 'string-width';\nimport sliceAnsi from 'slice-ansi';\nimport {getVersion} from '../version.ts';\nimport type {configType} from '../types.ts';\n\n// ── TTY\nconst IS_TTY = process.stdout.isTTY === true;\n\n// ── Icons\n// Authoritative form is the hex escape; emoji in comments are for readability only.\n// ICON_GEAR uses U+2699 without VS-16 (U+FE0F) intentionally — the variation selector\n// forces emoji presentation and makes string-width report width=2. Without it the\n// character is \"ambiguous\" and resolves to width=1 via the ambiguousIsNarrow option below.\nconst ICON_GLOBE = '\\u{1F310}'; // 🌐\nconst ICON_KEY = '\\u{1F511}'; // 🔑\nconst ICON_DOC = '\\u{1F4C4}'; // 📄\nconst ICON_PACKAGE = '\\u{1F4E6}'; // 📦\nconst ICON_LINK = '\\u{1F517}'; // 🔗\nconst ICON_FOLDER = '\\u{1F4C1}'; // 📁\nconst ICON_GEAR = '\\u{1F527}'; // 🔧\nconst ICON_HOME = '\\u{1F3E0}'; // 🏠\n\n// ── Layout\n// Each row: │(1) space(1) label(35) sep(1) value(40) space(1) │(1) = 80\nconst W = 80;\nconst LABEL_W = 35;\nconst VALUE_W = W - LABEL_W - 5; // 5 = left-border + left-space + separator + right-space + right-border\n\nconst BOX = {\n\th: '─',\n\tv: '│',\n\ttl: '╭',\n\ttr: '╮',\n\tbl: '╰',\n\tbr: '╯',\n\tml: '├',\n\tmr: '┤',\n} as const;\n\n/* Returns the visible terminal column-width of `s`. */\nconst displayWidth = (s: string): number => stringWidth(s, {ambiguousIsNarrow: true});\n\n/* Pads `s` with trailing spaces to `w` visible columns. Returns `s` unchanged if already ≥ `w`. */\nconst padTo = (s: string, w: number): string => {\n\tconst delta = w - displayWidth(s);\n\treturn delta > 0 ? s + ' '.repeat(delta) : s;\n};\n\n/* Truncates `s` to at most `w` visible columns. */\nconst truncateTo = (s: string, w: number): string => (w > 0 ? sliceAnsi(s, 0, w) : '');\n\n/* Mid-box horizontal divider. */\nconst divider = (): string => (IS_TTY ? chalk.dim(BOX.ml + BOX.h.repeat(W - 2) + BOX.mr) : '-'.repeat(W));\n\n/* Opening border. */\nconst borderOpen = (): string => (IS_TTY ? chalk.dim(BOX.tl + BOX.h.repeat(W - 2) + BOX.tr) : divider());\n\n/* Closing border. */\nconst borderClose = (): string => (IS_TTY ? chalk.dim(BOX.bl + BOX.h.repeat(W - 2) + BOX.br) : divider());\n\n/**\n * Single key/value row.\n * @param key   - Label text.\n * @param value - Display value; null/undefined/empty renders as a dim dash.\n * @param icon  - Optional leading icon (must be a single-cell or double-cell glyph).\n * @returns Formatted string.\n */\nconst row = (key: string, value: string | number | null | undefined, icon?: string): string => {\n\tconst left = IS_TTY ? (icon ? `${icon} ${key}` : `  ${key}`) : `${key}`;\n\tconst label = padTo(left, LABEL_W);\n\tconst hasValue = value !== null && value !== undefined && value !== '';\n\tconst valueText = hasValue ? String(value) : '—';\n\tconst colorFn = hasValue ? chalk.white : chalk.dim;\n\tconst valueCell = padTo(truncateTo(valueText, VALUE_W), VALUE_W);\n\n\treturn IS_TTY ? chalk.dim(BOX.v) + ' ' + chalk.dim(label) + ' ' + colorFn(valueCell) + ' ' + chalk.dim(BOX.v) : label + ' ' + valueCell;\n};\n\n/**\n * Renders the server startup banner to stdout.\n * @param cfg - Server configuration.\n */\nexport const printBanner = (cfg: configType): void => {\n\tconst adminRoute = cfg.adminRoute ?? '/admin';\n\tconst baseUrl = `http://localhost:${cfg.port}`;\n\tconst lines: string[] = [];\n\n\t// ── top border\n\tlines.push(borderOpen());\n\n\t// ── title\n\tconst title = `NODE PL/SQL SERVER ${getVersion()}`;\n\tconst pad = W - 2 - displayWidth(title);\n\tlines.push(IS_TTY ? chalk.dim(BOX.v) + ' '.repeat(Math.floor(pad / 2)) + chalk.bold.cyan(title) + ' '.repeat(Math.ceil(pad / 2)) + chalk.dim(BOX.v) : title);\n\tlines.push(divider());\n\n\t// ── server\n\tlines.push(row('Port', cfg.port, ICON_GLOBE));\n\tlines.push(row('Admin route', `${adminRoute}${cfg.adminUser ? ' (authenticated)' : ''}`, ICON_KEY));\n\tlines.push(row('Access log', cfg.loggerFilename, ICON_DOC));\n\tlines.push(row('Upload limit', typeof cfg.uploadFileSizeLimit === 'number' ? `${cfg.uploadFileSizeLimit} bytes` : null, ICON_PACKAGE));\n\tlines.push(divider());\n\n\t// ── connection pool\n\tlines.push(row('Oracle pool  min', cfg.oracle.poolMin, ICON_LINK));\n\tlines.push(row('Oracle pool  max', cfg.oracle.poolMax, ICON_LINK));\n\tlines.push(row('Oracle pool  increment', cfg.oracle.poolIncrement, ICON_LINK));\n\n\t// ── static routes\n\tif (cfg.routeStatic.length > 0) {\n\t\tlines.push(divider());\n\t\tcfg.routeStatic.forEach((r, i) => {\n\t\t\tlines.push(row(`Static route #${i + 1}  route`, r.route, ICON_FOLDER));\n\t\t\tlines.push(row(`Static route #${i + 1}  path`, r.directoryPath));\n\t\t});\n\t}\n\n\t// ── PL/SQL routes\n\tif (cfg.routePlSql.length > 0) {\n\t\tlines.push(divider());\n\t\tcfg.routePlSql.forEach((r, i) => {\n\t\t\tconst txMode = typeof r.transactionMode === 'string' ? r.transactionMode : r.transactionMode ? 'custom' : '';\n\t\t\tconst n = `PL/SQL route #${i + 1}`;\n\n\t\t\tlines.push(row(`${n}  route`, r.route, ICON_GEAR));\n\t\t\tlines.push(row(`${n}  Oracle user`, r.user));\n\t\t\tlines.push(row(`${n}  Oracle server`, r.connectString));\n\t\t\tlines.push(row(`${n}  document table`, r.documentTable));\n\t\t\tlines.push(row(`${n}  default page`, r.defaultPage));\n\t\t\tlines.push(row(`${n}  path alias`, r.pathAlias ?? ''));\n\t\t\tlines.push(row(`${n}  path alias proc`, r.pathAliasProcedure ?? ''));\n\t\t\tlines.push(row(`${n}  exclusion list`, r.exclusionList?.join(', ') ?? ''));\n\t\t\tlines.push(row(`${n}  validation fn`, r.requestValidationFunction ?? ''));\n\t\t\tlines.push(row(`${n}  session mode`, txMode));\n\t\t\tlines.push(row(`${n}  auth`, typeof r.auth === 'string' ? r.auth : ''));\n\t\t\tlines.push(row(`${n}  error style`, r.errorStyle));\n\t\t});\n\t}\n\n\t// ── footer: quick-access URLs\n\tlines.push(divider());\n\tlines.push(row('Admin console', `${baseUrl}${adminRoute}`, ICON_HOME));\n\tcfg.routePlSql.forEach((r) => {\n\t\tlines.push(row(r.route, `${baseUrl}${r.route}`, ICON_GEAR));\n\t});\n\tlines.push(borderClose());\n\n\tconsole.log(lines.join('\\n'));\n};","import debugModule from 'debug';\nconst debug = debugModule('webplsql:server');\n\nimport http from 'node:http';\nimport https from 'node:https';\nimport type {Socket} from 'node:net';\n\nimport express, {type Express, type Request, type Response, type NextFunction} from 'express';\nimport type {Pool} from 'oracledb';\nimport cors from 'cors';\nimport cookieParser from 'cookie-parser';\nimport compression from 'compression';\nimport expressStaticGzip from 'express-static-gzip';\n\n// NOTE: it is only allowed to import from the API './index.ts'\nimport {\n\thandlerWebPlSql,\n\thandlerAdminConsole,\n\thandlerUpload,\n\thandlerLogger,\n\tAdminContext,\n\tprintBanner,\n\treadFileSyncUtf8,\n\tgetJsonFile,\n\tinstallShutdown,\n\tz$configType,\n\ttype configInputType,\n\ttype configType,\n\ttype configPlSqlType,\n\toracledb,\n\tcreateSpaFallback,\n} from '../index.ts';\n\n/**\n * Close multiple pools.\n * @param pools - The pools to close.\n */\nexport const poolsClose = async (pools: Pool[]): Promise<void> => {\n\tawait Promise.all(pools.map((pool) => pool.close(0)));\n};\n\nexport type webServer = {\n\tconfig: configType;\n\tconnectionPools: Pool[];\n\tapp: Express;\n\tserver: http.Server | https.Server;\n\tadminContext: AdminContext;\n\tshutdown: () => Promise<void>;\n};\n\nexport type sslConfig = {\n\tkeyFilename: string;\n\tcertFilename: string;\n};\n\n/**\n * Create HTTPS server.\n * @param app - express application\n * @param ssl - ssl configuration.\n * @returns server\n */\nexport const createServer = (app: Express, ssl?: sslConfig): http.Server | https.Server => {\n\tif (ssl) {\n\t\tconst key = readFileSyncUtf8(ssl.keyFilename);\n\t\tconst cert = readFileSyncUtf8(ssl.certFilename);\n\n\t\treturn https.createServer({key, cert}, app);\n\t} else {\n\t\treturn http.createServer(app);\n\t}\n};\n\n/**\n * Start server.\n * @param config - The config.\n * @param ssl - ssl configuration.\n * @returns Promise resolving to the web server object.\n */\nexport const startServer = async (config: configInputType, ssl?: sslConfig): Promise<webServer> => {\n\tdebug('startServer: BEGIN', config, ssl);\n\n\tconst internalConfig = z$configType.parse(config);\n\n\tprintBanner(internalConfig);\n\n\t// Create express app\n\tconst app = express();\n\n\t// Default middleware\n\tif (internalConfig.devMode) {\n\t\tapp.use(\n\t\t\tcors({\n\t\t\t\torigin: 'http://localhost:5173',\n\t\t\t\tcredentials: true,\n\t\t\t}),\n\t\t);\n\t}\n\tapp.use(handlerUpload(internalConfig.uploadFileSizeLimit));\n\tapp.use(express.json({limit: '50mb'}));\n\tapp.use(express.urlencoded({limit: '50mb', extended: true}));\n\tapp.use(cookieParser());\n\tapp.use(compression());\n\n\t// Create AdminContext\n\tconst adminContext = new AdminContext(internalConfig);\n\n\t// Mount Admin Console (includes Pause middleware)\n\tapp.use(\n\t\thandlerAdminConsole(\n\t\t\t{\n\t\t\t\tadminRoute: internalConfig.adminRoute,\n\t\t\t\tuser: internalConfig.adminUser,\n\t\t\t\tpassword: internalConfig.adminPassword,\n\t\t\t\tdevMode: internalConfig.devMode,\n\t\t\t},\n\t\t\tadminContext,\n\t\t),\n\t);\n\n\t// Oracle pl/sql express middleware\n\tfor (const i of internalConfig.routePlSql) {\n\t\t// Allocate the Oracle database pool\n\t\tconst pool = await oracledb.createPool({\n\t\t\tuser: i.user,\n\t\t\tpassword: i.password,\n\t\t\tconnectString: i.connectString,\n\t\t\tpoolMin: internalConfig.oracle.poolMin,\n\t\t\tpoolMax: internalConfig.oracle.poolMax,\n\t\t\tpoolIncrement: internalConfig.oracle.poolIncrement,\n\t\t});\n\n\t\tconst handler = handlerWebPlSql(pool, i as configPlSqlType, adminContext);\n\n\t\tapp.use([`${i.route}/:name`, i.route], (req: Request, res: Response, next: NextFunction) => {\n\t\t\tconst start = process.hrtime();\n\t\t\tres.on('finish', () => {\n\t\t\t\tconst diff = process.hrtime(start);\n\t\t\t\tconst duration = diff[0] * 1000 + diff[1] / 1_000_000;\n\t\t\t\tadminContext.statsManager.recordRequest(duration, res.statusCode >= 400);\n\t\t\t});\n\t\t\thandler(req, res, next);\n\t\t});\n\t}\n\n\t// Access log\n\tif (internalConfig.loggerFilename.length > 0) {\n\t\tapp.use(handlerLogger(internalConfig.loggerFilename));\n\t}\n\n\t// Execute custom extensions before static/SPA routes capture the request\n\tif (internalConfig.setupExtensions) {\n\t\tawait internalConfig.setupExtensions(app, adminContext.pools);\n\t}\n\n\t// Serving static files\n\tfor (const i of internalConfig.routeStatic) {\n\t\tapp.use(\n\t\t\ti.route,\n\t\t\texpressStaticGzip(i.directoryPath, {\n\t\t\t\tenableBrotli: true,\n\t\t\t\torderPreference: ['br'],\n\t\t\t}),\n\t\t);\n\n\t\t// Mount SPA fallback (serves index.html for unmatched routes)\n\t\t// IMPORTANT: Must come AFTER expressStaticGzip\n\t\tif (i.spaFallback) {\n\t\t\tapp.use(i.route, createSpaFallback(i.directoryPath, i.route));\n\t\t}\n\t}\n\n\t// Mount PL/SQL handlers with stats tracking\n\t// (already mounted in the loop above)\n\n\t// create server\n\tdebug('startServer: createServer');\n\tconst server = createServer(app, ssl);\n\n\t// Track open connections\n\tconst connections = new Set<Socket>();\n\tserver.on('connection', (socket: Socket) => {\n\t\tconnections.add(socket);\n\t\tsocket.on('close', () => {\n\t\t\tconnections.delete(socket);\n\t\t});\n\t});\n\n\tconst closeAllConnections = () => {\n\t\tfor (const socket of connections) {\n\t\t\tsocket.destroy(); // forcibly closes the connection\n\t\t\tconnections.delete(socket);\n\t\t}\n\t};\n\n\tconst shutdown = async () => {\n\t\tdebug('startServer: onShutdown');\n\n\t\tadminContext.statsManager.stop();\n\n\t\tawait poolsClose(adminContext.pools);\n\n\t\tserver.close(() => {\n\t\t\tconsole.log('Server has closed');\n\t\t\tprocess.exit(0);\n\t\t});\n\n\t\tcloseAllConnections();\n\t};\n\n\t// Install shutdown handler\n\tinstallShutdown(shutdown);\n\n\t// Listen\n\tdebug('startServer: start listener');\n\tawait new Promise<void>((resolve, reject) => {\n\t\tserver\n\t\t\t.listen(internalConfig.port)\n\t\t\t.on('listening', () => {\n\t\t\t\tdebug('startServer: listener running');\n\t\t\t\tresolve();\n\t\t\t})\n\t\t\t.on('error', (err: NodeJS.ErrnoException) => {\n\t\t\t\tif ('code' in err) {\n\t\t\t\t\tif (err.code === 'EADDRINUSE') {\n\t\t\t\t\t\terr.message = `Port ${internalConfig.port} is already in use`;\n\t\t\t\t\t} else if (err.code === 'EACCES') {\n\t\t\t\t\t\terr.message = `Port ${internalConfig.port} requires elevated privileges`;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treject(err);\n\t\t\t});\n\t});\n\n\tdebug('startServer: END');\n\n\treturn {\n\t\tconfig: internalConfig,\n\t\tconnectionPools: adminContext.pools,\n\t\tapp,\n\t\tserver,\n\t\tadminContext,\n\t\tshutdown,\n\t};\n};\n\n/**\n * Load configuration.\n * @param filename - The configuration filename.\n * @returns Promise.\n */\nexport const loadConfig = (filename = 'config.json'): configType => z$configType.parse(getJsonFile(filename));\n\n/**\n * Start server from config file.\n * @param filename - The configuration filename.\n * @param ssl - ssl configuration.\n * @returns Promise resolving to the web server object.\n */\nexport const startServerConfig = async (filename = 'config.json', ssl?: sslConfig): Promise<webServer> => startServer(loadConfig(filename), ssl);","import debugModule from 'debug';\nconst debug = debugModule('webplsql:shutdown');\n\n/**\n * Install a shutdown handler.\n * @param handler - Shutdown handler\n */\nexport const installShutdown = (handler: () => Promise<void>): void => {\n\tdebug('installShutdown');\n\n\tlet isShuttingDown = false;\n\n\t/*\n\t *\tThe '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.\n\t */\n\tprocess.on('unhandledRejection', (reason) => {\n\t\tif (isShuttingDown) {\n\t\t\treturn;\n\t\t}\n\t\tisShuttingDown = true;\n\n\t\tif (reason instanceof Error) {\n\t\t\tconsole.error(`\\n${reason.message}. Graceful shutdown...`);\n\t\t} else {\n\t\t\tconsole.error('\\nUnhandled promise rejection. Graceful shutdown...', reason);\n\t\t}\n\t\tvoid handler().catch((err: unknown) => {\n\t\t\tconsole.error('Error during shutdown:', err);\n\t\t\tprocess.exit(1);\n\t\t});\n\t});\n\n\t// install signal event handler\n\tprocess.on('SIGTERM', function sigterm() {\n\t\tif (isShuttingDown) {\n\t\t\treturn;\n\t\t}\n\t\tisShuttingDown = true;\n\n\t\tconsole.log('\\nGot SIGTERM (aka docker container stop). Graceful shutdown...');\n\t\tvoid handler().catch((err: unknown) => {\n\t\t\tconsole.error('Error during shutdown:', err);\n\t\t\tprocess.exit(1);\n\t\t});\n\t});\n\n\tprocess.on('SIGINT', function sigint() {\n\t\tif (isShuttingDown) {\n\t\t\treturn;\n\t\t}\n\t\tisShuttingDown = true;\n\n\t\tconsole.log('\\nGot SIGINT (aka ctrl-c in docker). Graceful shutdown...');\n\t\tvoid handler().catch((err: unknown) => {\n\t\t\tconsole.error('Error during shutdown:', err);\n\t\t\tprocess.exit(1);\n\t\t});\n\t});\n};\n\n/**\n * Force a shutdown.\n */\nexport const forceShutdown = (): void => {\n\tdebug('forceShutdown');\n\n\tprocess.kill(process.pid, 'SIGTERM');\n};","/**\n * Web PL/SQL Gateway - Common Shared Constants\n *\n * This file centralizes all hardcoded numeric and string constants used throughout\n * the application. Constants are organized by functional category.\n */\n\n// =============================================================================\n// CACHE CONFIGURATION\n// =============================================================================\n\n/**\n * DEFAULT_CACHE_MAX_SIZE = 10000\n *\n * Purpose: Maximum number of entries in the generic LFU (Least Frequently Used) cache.\n *\n * Used By:\n *   - procedureNameCache: Caches resolved Oracle procedure names (e.g., \"HR.EMPLOYEES\")\n *   - argumentCache: Caches procedure argument introspection results from all_arguments view\n *\n * Related Values:\n *   - CACHE_PRUNE_PERCENT (0.1): When cache is full, removes 10% = 1000 entries\n *   - Cache instantiation in src/handler/plsql/handlerPlSql.js creates new Cache() without params\n *\n * Implications:\n *   - Memory footprint: ~1-2MB at max capacity (strings + hitCount metadata)\n *   - Pruning: Removes least-frequently-used entries when full\n *   - Higher values = better cache hit rates but more memory\n */\nexport const DEFAULT_CACHE_MAX_SIZE = 10_000;\n\n/**\n * CACHE_PRUNE_PERCENT = 0.1\n *\n * Purpose: Fraction of cache entries to remove during pruning (10%).\n *\n * Used By: Cache.prune() method only\n *\n * Related Values:\n *   - DEFAULT_CACHE_MAX_SIZE (10000): Applied to this value to calculate removeCount = 1000\n *\n * Implications:\n *   - Balances between removing too few entries (frequent pruning) vs too many (evicting useful data)\n *   - 10% is a common pattern for cache eviction\n */\nexport const CACHE_PRUNE_PERCENT = 0.1;\n\n// =============================================================================\n// ORACLE LIMITS\n// =============================================================================\n\n/**\n * MAX_PROCEDURE_PARAMETERS = 1000\n *\n * Purpose: Maximum number of procedure arguments that can be introspected from Oracle's\n *          all_arguments view using BULK COLLECT with dbms_utility.name_resolve.\n *\n * Used By:\n *   - src/handler/plsql/procedureNamed.js\n *     bind.names = {maxArraySize: MAX_PARAMETER_NUMBER}\n *     bind.types = {maxArraySize: MAX_PARAMETER_NUMBER}\n *\n * Related Values:\n *   - Procedure introspection SQL: SQL_GET_ARGUMENT block at procedureNamed.js:27-43\n *   - oracledb.BIND_OUT direction for array fetches\n *\n * Implications:\n *   - This is an Oracle driver limitation for array binding, not an arbitrary choice\n *   - Procedures with >1000 arguments will have introspection truncated\n *   - No error handling exists for this edge case\n *   - Practical limit: most procedures have <50 arguments\n */\nexport const MAX_PROCEDURE_PARAMETERS = 1000;\n\n/**\n * OWA_STREAM_CHUNK_SIZE = 1000\n *\n * Purpose: Number of lines fetched per Oracle OWA call when streaming page content.\n *\n * Used By:\n *   - owaPageStream.js: maxArraySize for :lines bind variable\n *   - owaPageStream.js: :irows INOUT parameter value\n *   - owaPageStream.js: Determines when streaming is complete (lines.length < chunkSize)\n *\n * Related Values:\n *   - OWA_GET_PAGE_SQL: 'BEGIN owa.get_page(thepage=>:lines, irows=>:irows); END;'\n *   - OWAPageStream class constructor at line 20\n *\n * Implications:\n *   - Controls round-trip frequency to Oracle database\n *   - Higher = fewer round-trips but larger memory buffers per fetch\n *   - Lower = more responsive streaming but more database calls\n *   - Each line is a PL/SQL varchar2; total data per chunk depends on htp.htbuf_len (63 chars)\n *   - Estimated max data per chunk: 1000 lines × 63 chars = 63KB\n */\nexport const OWA_STREAM_CHUNK_SIZE = 1000;\n\n// =============================================================================\n// STREAMING\n// =============================================================================\n\n/**\n * OWA_STREAM_BUFFER_SIZE = 16384\n *\n * Purpose: Node.js Readable stream highWaterMark in bytes (16KB).\n *\n * Used By: OWAPageStream class extends Readable stream\n *\n * Related Values:\n *   - OWA_STREAM_CHUNK_SIZE (1000): Lines per fetch\n *   - Default Node.js highWaterMark is 64KB (Readable stream default)\n *   - OWAPageStream.push() converts lines to string buffer\n *\n * Implications:\n *   - Smaller than default (64KB) = more frequent _read() callbacks\n *   - Reduces memory footprint for large responses\n *   - Improves backpressure handling responsiveness\n *   - Trade-off: More CPU for _read() calls vs memory efficiency\n */\nexport const OWA_STREAM_BUFFER_SIZE = 16_384;\n\n/**\n * OWA_RESOLVED_NAME_MAX_LEN = 400\n *\n * Purpose: Maximum string length for resolved Oracle procedure canonical names.\n *          Canonical format: SCHEMA.PACKAGE.PROCEDURE or SCHEMA.PROCEDURE.\n *\n * Used By:\n *   - resolveProcedureName() function for dbms_utility.name_resolve output\n *   - Procedure name resolution SQL at procedureSanitize.js:46-76\n *\n * Related Values:\n *   - dbms_utility.name_resolve context = 1 (procedure/function resolution)\n *   - Oracle identifier limits: Schema (128) + Package (128) + Procedure (128) + 2 dots = ~386\n *   - 400 provides comfortable headroom\n *\n * Implications:\n *   - Oracle object names: 30 bytes for most, extended to 128 in some contexts\n *   - Canonical name: schema.package.procedure (max ~128+1+128+1+128 = 386)\n *   - 400 is safe upper bound with margin\n */\nexport const OWA_RESOLVED_NAME_MAX_LEN = 400;\n\n// =============================================================================\n// STATS COLLECTION\n// =============================================================================\n\n/**\n * STATS_INTERVAL_MS = 5000\n *\n * Purpose: Duration of each statistical bucket in milliseconds.\n *\n * Used By:\n *   - src/util/statsManager.js:165: setInterval(this.rotateBucket, this.config.intervalMs)\n *   - src/handler/handlerAdmin.js:123: Exposed as intervalMs in /api/status response\n *   - src/frontend/main.ts\n *   - src/frontend/charts.ts\n *\n * Related Values:\n *   - MAX_HISTORY_BUCKETS (1000): At 5s per bucket = ~83 minutes of history\n *   - MAX_PERCENTILE_SAMPLES (1000): Samples per bucket for P95/P99\n *\n * Implications:\n *   - Bucket aggregation: request counts, durations, errors, system metrics\n *   - Affects granularity of performance monitoring\n *   - Lower values = more granular but more history entries\n *   - Higher values = smoother averages but less detail\n */\nexport const STATS_INTERVAL_MS = 5000;\n\n/**\n * MAX_HISTORY_BUCKETS = 1000\n *\n * Purpose: Maximum number of statistical buckets retained in StatsManager ring buffer.\n *\n * Used By:\n *   - src/util/statsManager.js: Ring buffer limit check\n *     if (this.history.length > this.config.maxHistoryPoints) { this.history.shift(); }\n *   - Exposed via /api/stats/history endpoint\n *\n * Related Values:\n *   - STATS_INTERVAL_MS (5000): 5s per bucket = ~83 minutes total history\n *   - Each bucket contains: timestamp, requestCount, errors, durations, system metrics\n *   - Bucket memory estimate: ~100 bytes × 1000 = ~100KB\n *\n * Implications:\n *   - Ring buffer: oldest bucket is removed when new one exceeds limit\n *   - Affects admin console chart history display\n *   - Higher = more historical context but more memory\n */\nexport const MAX_HISTORY_BUCKETS = 1000;\n\n/**\n * MAX_PERCENTILE_SAMPLES = 1000\n *\n * Purpose: Maximum number of request duration samples collected per bucket\n *          for calculating P95/P99 percentiles.\n *\n * Used By:\n *   - src/util/statsManager.js: Array length check\n *     if (b.durations.length < this.config.percentilePrecision)\n *   - src/util/statsManager.js: P95/P99 calculation\n *\n * Related Values:\n *   - Percentile calculation: floor(length × 0.95) and floor(length × 0.99)\n *   - FIFO replacement: When exceeded, new samples replace oldest\n *\n * Implications:\n *   - With 1000 samples, P95/P99 are statistically meaningful\n *   - Higher = more accurate percentiles but more memory per bucket\n *   - With STATS_INTERVAL_MS = 5000, 1000 samples ≈ 5 req/sec sustained\n *   - Under heavy load, older samples are discarded (FIFO)\n */\nexport const MAX_PERCENTILE_SAMPLES = 1000;\n\n// =============================================================================\n// SHUTDOWN\n// =============================================================================\n\n/**\n * SHUTDOWN_GRACE_DELAY_MS = 100\n *\n * Purpose: Delay between initiating server shutdown and forced termination.\n *\n * Used By: POST /api/server/stop handler only\n *\n * Related Values:\n *   - forceShutdown() at src/util/shutdown.js\n *   - SIGTERM/SIGINT handlers at shutdown.js\n *\n * Implications:\n *   - Allows graceful completion of in-flight requests\n *   - Gives Express middleware time to send final responses\n *   - 100ms is short; may be insufficient under heavy load\n *   - Consider making configurable for high-traffic deployments\n */\nexport const SHUTDOWN_GRACE_DELAY_MS = 100;\n\n// =============================================================================\n// LOG ROTATION - TRACE\n// =============================================================================\n\n/**\n * TRACE_LOG_ROTATION_SIZE = '10M'\n *\n * Purpose: Log file size threshold triggering trace log rotation (10 Megabytes).\n *\n * Used By: rotating-file-stream library for 'trace.log'\n *\n * Related Values:\n *   - TRACE_LOG_ROTATION_INTERVAL ('1d'): Also triggers rotation\n *   - TRACE_LOG_MAX_ROTATED_FILES (10): Maximum retained files\n *\n * Implications:\n *   - When either size OR time threshold is reached, rotation occurs\n *   - Combined with daily rotation: ~10MB/day minimum\n *   - gzip compression reduces rotated file size by ~70-90%\n */\nexport const TRACE_LOG_ROTATION_SIZE = '10M';\n\n/**\n * TRACE_LOG_ROTATION_INTERVAL = '1d'\n *\n * Purpose: Time-based trace log rotation trigger (daily).\n *\n * Used By: rotating-file-stream library for 'trace.log'\n *\n * Implications:\n *   - Guarantees at least one rotation per day\n *   - Midnight-based or 24h from first write\n */\nexport const TRACE_LOG_ROTATION_INTERVAL = '1d';\n\n/**\n * TRACE_LOG_MAX_ROTATED_FILES = 10\n *\n * Purpose: Maximum number of rotated trace log files to retain.\n *\n * Used By: rotating-file-stream library for 'trace.log'\n *\n * Implications:\n *   - When exceeded, oldest rotated file is deleted\n *   - Maximum: ~10 files × 10MB = ~100MB (compressed: ~10-30MB)\n */\nexport const TRACE_LOG_MAX_ROTATED_FILES = 10;\n\n// =============================================================================\n// LOG ROTATION - JSON\n// =============================================================================\n\n/**\n * JSON_LOG_ROTATION_SIZE = '10M'\n *\n * Purpose: Log file size threshold triggering JSON error log rotation (10 Megabytes).\n *\n * Used By: rotating-file-stream library for 'error.json.log'\n *\n * Related Values:\n *   - JSON_LOG_ROTATION_INTERVAL ('1d'): Also triggers rotation\n *   - JSON_LOG_MAX_ROTATED_FILES (10): Maximum retained files\n *\n * Implications:\n *   - When either size OR time threshold is reached, rotation occurs\n *   - Combined with daily rotation: ~10MB/day minimum\n *   - gzip compression reduces rotated file size by ~70-90%\n */\nexport const JSON_LOG_ROTATION_SIZE = '10M';\n\n/**\n * JSON_LOG_ROTATION_INTERVAL = '1d'\n *\n * Purpose: Time-based JSON error log rotation trigger (daily).\n *\n * Used By: rotating-file-stream library for 'error.json.log'\n *\n * Implications:\n *   - Guarantees at least one rotation per day\n *   - Midnight-based or 24h from first write\n */\nexport const JSON_LOG_ROTATION_INTERVAL = '1d';\n\n/**\n * JSON_LOG_MAX_ROTATED_FILES = 10\n *\n * Purpose: Maximum number of rotated JSON error log files to retain.\n *\n * Used By: rotating-file-stream library for 'error.json.log'\n *\n * Implications:\n *   - When exceeded, oldest rotated file is deleted\n *   - Maximum: ~10 files × 10MB = ~100MB (compressed: ~10-30MB)\n */\nexport const JSON_LOG_MAX_ROTATED_FILES = 10;","import debugModule from 'debug';\nimport os from 'node:os';\nimport {STATS_INTERVAL_MS, MAX_HISTORY_BUCKETS, MAX_PERCENTILE_SAMPLES} from '../../common/constants.ts';\n\nconst debug = debugModule('webplsql:statsManager');\n\ntype StatsConfig = {\n\tintervalMs: number;\n\tmaxHistoryPoints: number;\n\tsampleSystem: boolean;\n\tsamplePools: boolean;\n\tpercentilePrecision: number;\n};\n\ntype CacheStats = {\n\tsize: number;\n\thits: number;\n\tmisses: number;\n};\n\ntype PoolCacheSnapshot = {\n\tprocedureName: CacheStats;\n\targument: CacheStats;\n};\n\nexport type PoolSnapshot = {\n\tname: string;\n\tconnectionsInUse: number;\n\tconnectionsOpen: number;\n\tcache?: PoolCacheSnapshot;\n};\n\ntype Bucket = {\n\ttimestamp: number;\n\trequests: number;\n\terrors: number;\n\tdurationMin: number;\n\tdurationMax: number;\n\tdurationAvg: number;\n\tdurationP95: number;\n\tdurationP99: number;\n\tsystem: {\n\t\tcpu: number;\n\t\theapUsed: number;\n\t\theapTotal: number;\n\t\trss: number;\n\t\texternal: number;\n\t};\n\tpools: PoolSnapshot[];\n};\n\ntype CurrentBucket = {\n\tcount: number;\n\terrors: number;\n\tdurationSum: number;\n\tdurationMin: number;\n\tdurationMax: number;\n\tdurations: number[];\n};\n\ntype MemoryLifetime = {\n\theapUsedMax: number;\n\theapTotalMax: number;\n\trssMax: number;\n\texternalMax: number;\n};\n\ntype LifetimeStats = {\n\ttotalRequests: number;\n\ttotalErrors: number;\n\tminDuration: number;\n\tmaxDuration: number;\n\ttotalDuration: number;\n\tmaxRequestsPerSecond: number;\n\tmemory: MemoryLifetime;\n\tcpu: {\n\t\tmax: number;\n\t\tuserMax: number;\n\t\tsystemMax: number;\n\t};\n};\n\ntype StatsSummary = {\n\tstartTime: Date;\n\ttotalRequests: number;\n\ttotalErrors: number;\n\tavgResponseTime: number;\n\tminResponseTime: number;\n\tmaxResponseTime: number;\n\tmaxRequestsPerSecond: number;\n\tmaxMemory: MemoryLifetime;\n\tcpu: {\n\t\tmax: number;\n\t\tuserMax: number;\n\t\tsystemMax: number;\n\t};\n};\n\n/**\n * Manager for statistical data collection and temporal bucketing.\n */\nexport class StatsManager {\n\tconfig: StatsConfig;\n\tstartTime: Date;\n\thistory: Bucket[];\n\tlifetime: LifetimeStats;\n\t_currentBucket: CurrentBucket;\n\t_lastCpuTimes: {user: number; nice: number; sys: number; idle: number; irq: number; total: number};\n\t_timer: NodeJS.Timeout | undefined;\n\n\t/**\n\t * @param config - Configuration.\n\t */\n\tconstructor(config: Partial<StatsConfig> = {}) {\n\t\tthis.config = {\n\t\t\tintervalMs: STATS_INTERVAL_MS,\n\t\t\tmaxHistoryPoints: MAX_HISTORY_BUCKETS,\n\t\t\tsampleSystem: true,\n\t\t\tsamplePools: true,\n\t\t\tpercentilePrecision: MAX_PERCENTILE_SAMPLES,\n\t\t\t...config,\n\t\t};\n\n\t\tthis.startTime = new Date();\n\t\tthis.history = [];\n\n\t\tthis.lifetime = {\n\t\t\ttotalRequests: 0,\n\t\t\ttotalErrors: 0,\n\t\t\tminDuration: -1,\n\t\t\tmaxDuration: -1,\n\t\t\ttotalDuration: 0,\n\t\t\tmaxRequestsPerSecond: 0,\n\t\t\tmemory: {\n\t\t\t\theapUsedMax: 0,\n\t\t\t\theapTotalMax: 0,\n\t\t\t\trssMax: 0,\n\t\t\t\texternalMax: 0,\n\t\t\t},\n\t\t\tcpu: {\n\t\t\t\tmax: 0,\n\t\t\t\tuserMax: 0,\n\t\t\t\tsystemMax: 0,\n\t\t\t},\n\t\t};\n\n\t\tthis._currentBucket = {\n\t\t\tcount: 0,\n\t\t\terrors: 0,\n\t\t\tdurations: [],\n\t\t\tdurationSum: 0,\n\t\t\tdurationMin: -1,\n\t\t\tdurationMax: -1,\n\t\t};\n\n\t\tthis._lastCpuTimes = this._getSystemCpuTimes();\n\n\t\tthis._timer = undefined;\n\t\tif (this.config.sampleSystem) {\n\t\t\tthis._timer = setInterval(() => {\n\t\t\t\tthis.rotateBucket();\n\t\t\t}, this.config.intervalMs);\n\t\t\tif (this._timer && typeof this._timer.unref === 'function') {\n\t\t\t\tthis._timer.unref();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Reset the current bucket accumulator.\n\t */\n\tprivate _resetBucket(): void {\n\t\tthis._currentBucket = {\n\t\t\tcount: 0,\n\t\t\terrors: 0,\n\t\t\tdurations: [],\n\t\t\tdurationSum: 0,\n\t\t\tdurationMin: -1,\n\t\t\tdurationMax: -1,\n\t\t};\n\t}\n\n\t/**\n\t * Record a request event.\n\t * @param duration - Duration in milliseconds.\n\t * @param isError - Whether the request was an error.\n\t */\n\trecordRequest(duration: number, isError = false): void {\n\t\tthis.lifetime.totalRequests++;\n\t\tif (isError) {\n\t\t\tthis.lifetime.totalErrors++;\n\t\t}\n\n\t\tthis.lifetime.totalDuration += duration;\n\t\tif (this.lifetime.minDuration < 0 || duration < this.lifetime.minDuration) {\n\t\t\tthis.lifetime.minDuration = duration;\n\t\t}\n\t\tif (this.lifetime.maxDuration < 0 || duration > this.lifetime.maxDuration) {\n\t\t\tthis.lifetime.maxDuration = duration;\n\t\t}\n\n\t\tconst b = this._currentBucket;\n\t\tb.count++;\n\t\tif (isError) {\n\t\t\tb.errors++;\n\t\t}\n\n\t\tb.durationSum += duration;\n\t\tif (b.durationMin < 0 || duration < b.durationMin) {\n\t\t\tb.durationMin = duration;\n\t\t}\n\t\tif (b.durationMax < 0 || duration > b.durationMax) {\n\t\t\tb.durationMax = duration;\n\t\t}\n\n\t\tif (b.durations.length < this.config.percentilePrecision) {\n\t\t\tb.durations.push(duration);\n\t\t}\n\t}\n\n\t/**\n\t * Get system CPU times.\n\t * @returns System CPU times.\n\t */\n\tprivate _getSystemCpuTimes(): {user: number; nice: number; sys: number; idle: number; irq: number; total: number} {\n\t\tconst cpus = os.cpus();\n\t\tlet user = 0;\n\t\tlet nice = 0;\n\t\tlet sys = 0;\n\t\tlet idle = 0;\n\t\tlet irq = 0;\n\n\t\tfor (const cpu of cpus) {\n\t\t\tuser += cpu.times.user;\n\t\t\tnice += cpu.times.nice;\n\t\t\tsys += cpu.times.sys;\n\t\t\tidle += cpu.times.idle;\n\t\t\tirq += cpu.times.irq;\n\t\t}\n\n\t\tconst total = user + nice + sys + idle + irq;\n\t\treturn {user, nice, sys, idle, irq, total};\n\t}\n\n\t/**\n\t * Calculate CPU usage percentage since last call.\n\t * @returns CPU usage percentage (0-100).\n\t */\n\tprivate _calculateCpuUsage(): number {\n\t\tconst current = this._getSystemCpuTimes();\n\t\tconst last = this._lastCpuTimes || {user: 0, nice: 0, sys: 0, idle: 0, irq: 0, total: 0};\n\n\t\tconst deltaTotal = current.total - last.total;\n\t\tconst deltaIdle = current.idle - last.idle;\n\n\t\tthis._lastCpuTimes = current;\n\n\t\tif (deltaTotal <= 0) return 0;\n\n\t\tconst percent = ((deltaTotal - deltaIdle) / deltaTotal) * 100;\n\t\treturn Math.min(100, Math.max(0, percent));\n\t}\n\n\t/**\n\t * Rotate the current bucket into history and start a new one.\n\t * @param poolSnapshots - Optional pool snapshots to include.\n\t */\n\trotateBucket(poolSnapshots: PoolSnapshot[] = []): void {\n\t\tconst b = this._currentBucket;\n\t\tconst memUsage = process.memoryUsage();\n\t\tconst systemMemoryUsed = os.totalmem() - os.freemem();\n\t\tconst cpuUsage = process.cpuUsage();\n\t\tconst cpu = this._calculateCpuUsage();\n\n\t\t// Update lifetime extremes\n\t\tconst reqPerSec = b.count / (this.config.intervalMs / 1000);\n\t\tthis.lifetime.maxRequestsPerSecond = Math.max(this.lifetime.maxRequestsPerSecond, reqPerSec);\n\t\tthis.lifetime.memory.heapUsedMax = Math.max(this.lifetime.memory.heapUsedMax, memUsage.heapUsed);\n\t\tthis.lifetime.memory.heapTotalMax = Math.max(this.lifetime.memory.heapTotalMax, memUsage.heapTotal);\n\t\tthis.lifetime.memory.rssMax = Math.max(this.lifetime.memory.rssMax, systemMemoryUsed);\n\t\tthis.lifetime.memory.externalMax = Math.max(this.lifetime.memory.externalMax, memUsage.external);\n\t\tthis.lifetime.cpu.max = Math.max(this.lifetime.cpu.max, cpu);\n\t\tthis.lifetime.cpu.userMax = Math.max(this.lifetime.cpu.userMax, cpuUsage.user);\n\t\tthis.lifetime.cpu.systemMax = Math.max(this.lifetime.cpu.systemMax, cpuUsage.system);\n\n\t\tlet p95 = 0;\n\t\tlet p99 = 0;\n\t\tif (b.durations.length > 0) {\n\t\t\tconst sorted = b.durations.toSorted((x, y) => x - y);\n\t\t\tconst p95Idx = Math.floor(sorted.length * 0.95);\n\t\t\tconst p99Idx = Math.floor(sorted.length * 0.99);\n\t\t\tconst lastIdx = sorted.length - 1;\n\n\t\t\tp95 = sorted[p95Idx] ?? sorted[lastIdx] ?? 0;\n\t\t\tp99 = sorted[p99Idx] ?? sorted[lastIdx] ?? 0;\n\t\t}\n\n\t\tconst bucket: Bucket = {\n\t\t\ttimestamp: Date.now(),\n\t\t\trequests: b.count,\n\t\t\terrors: b.errors,\n\t\t\tdurationMin: Math.max(b.durationMin, 0),\n\t\t\tdurationMax: Math.max(b.durationMax, 0),\n\t\t\tdurationAvg: b.count > 0 ? b.durationSum / b.count : 0,\n\t\t\tdurationP95: p95,\n\t\t\tdurationP99: p99,\n\t\t\tsystem: {\n\t\t\t\tcpu,\n\t\t\t\theapUsed: memUsage.heapUsed,\n\t\t\t\theapTotal: memUsage.heapTotal,\n\t\t\t\trss: systemMemoryUsed,\n\t\t\t\texternal: memUsage.external,\n\t\t\t},\n\t\t\tpools: poolSnapshots,\n\t\t};\n\n\t\tthis.history.push(bucket);\n\t\tif (this.history.length > this.config.maxHistoryPoints) {\n\t\t\tthis.history.shift();\n\t\t}\n\n\t\tthis._resetBucket();\n\t\tdebug('Bucket rotated: %j', bucket);\n\t}\n\n\t/**\n\t * Stop the background timer.\n\t */\n\tstop(): void {\n\t\tif (this._timer) {\n\t\t\tclearInterval(this._timer);\n\t\t\tthis._timer = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Get lifetime summary.\n\t * @returns Summary.\n\t */\n\tgetSummary(): StatsSummary {\n\t\treturn {\n\t\t\tstartTime: this.startTime,\n\t\t\ttotalRequests: this.lifetime.totalRequests,\n\t\t\ttotalErrors: this.lifetime.totalErrors,\n\t\t\tavgResponseTime: this.lifetime.totalRequests > 0 ? this.lifetime.totalDuration / this.lifetime.totalRequests : 0,\n\t\t\tminResponseTime: this.lifetime.minDuration,\n\t\t\tmaxResponseTime: this.lifetime.maxDuration,\n\t\t\tmaxRequestsPerSecond: this.lifetime.maxRequestsPerSecond,\n\t\t\tmaxMemory: this.lifetime.memory,\n\t\t\tcpu: this.lifetime.cpu,\n\t\t};\n\t}\n\n\t/**\n\t * Get history buffer.\n\t * @returns The history buffer.\n\t */\n\tgetHistory(): Bucket[] {\n\t\treturn this.history;\n\t}\n}","import {StatsManager} from '../util/statsManager.ts';\nimport type {Pool} from 'oracledb';\nimport type {configType, argsType} from '../types.ts';\nimport type {Cache} from '../util/cache.ts';\n\ntype PoolCacheEntry = {\n\tpoolName: string;\n\tprocedureNameCache: Cache<string>;\n\targumentCache: Cache<argsType>;\n};\n\n/**\n * Admin Context Class\n */\nexport class AdminContext {\n\treadonly startTime: Date;\n\treadonly config: configType;\n\treadonly pools: Pool[];\n\treadonly caches: PoolCacheEntry[];\n\treadonly statsManager: StatsManager;\n\tprivate _paused: boolean;\n\n\tconstructor(config: configType) {\n\t\tthis.startTime = new Date();\n\t\tthis.config = config;\n\t\tthis.pools = [];\n\t\tthis.caches = [];\n\t\tthis.statsManager = new StatsManager();\n\t\tthis._paused = false;\n\t}\n\n\t/**\n\t * Register a PL/SQL handler with the admin context.\n\t * @param route - The route for the handler.\n\t * @param pool - The connection pool.\n\t * @param procedureNameCache - The procedure name cache.\n\t * @param argumentCache - The argument cache.\n\t */\n\tregisterHandler(route: string, pool: Pool, procedureNameCache: Cache<string>, argumentCache: Cache<argsType>): void {\n\t\tthis.pools.push(pool);\n\t\tthis.caches.push({\n\t\t\tpoolName: route,\n\t\t\tprocedureNameCache,\n\t\t\targumentCache,\n\t\t});\n\t}\n\n\tget paused(): boolean {\n\t\treturn this._paused;\n\t}\n\n\tsetPaused(value: boolean): void {\n\t\tthis._paused = value;\n\t}\n}","import {promises as fs, readFileSync} from 'node:fs';\n\n/**\n * Read file.\n *\n * @param filePath - File name.\n * @returns The string.\n */\nexport const readFileSyncUtf8 = (filePath: string): string => {\n\ttry {\n\t\treturn readFileSync(filePath, 'utf8');\n\t} catch {\n\t\tthrow new Error(`Unable to read file \"${filePath}\"`);\n\t}\n};\n\n/**\n * Read file.\n *\n * @param filePath - File name.\n * @returns The buffer.\n */\nexport const readFile = async (filePath: string): Promise<Buffer> => {\n\ttry {\n\t\treturn await fs.readFile(filePath);\n\t} catch {\n\t\tthrow new Error(`Unable to read file \"${filePath}\"`);\n\t}\n};\n\n/**\n * Remove file.\n *\n * @param filePath - File name.\n */\nexport const removeFile = async (filePath: string): Promise<void> => {\n\ttry {\n\t\tawait fs.unlink(filePath);\n\t} catch {\n\t\tthrow new Error(`Unable to remove file \"${filePath}\"`);\n\t}\n};\n\n/**\n * Load a json file.\n *\n * @param filePath - File name.\n * @returns The json object.\n */\nexport const getJsonFile = (filePath: string): unknown => {\n\ttry {\n\t\tconst fileContent = readFileSync(filePath, 'utf8');\n\t\treturn JSON.parse(fileContent);\n\t} catch {\n\t\tthrow new Error(`Unable to load file \"${filePath}\"`);\n\t}\n};\n\n/**\n * Is this a directory.\n * @param directoryPath - Directory name.\n * @returns Return true if it is a directory.\n */\nexport const isDirectory = async (directoryPath: unknown): Promise<boolean> => {\n\tif (typeof directoryPath !== 'string') {\n\t\treturn false;\n\t}\n\n\tconst stats = await fs.stat(directoryPath);\n\n\treturn stats.isDirectory();\n};\n\n/**\n * Is this a file.\n * @param filePath - File name.\n * @returns Return true if it is a file.\n */\nexport const isFile = async (filePath: unknown): Promise<boolean> => {\n\tif (typeof filePath !== 'string') {\n\t\treturn false;\n\t}\n\n\ttry {\n\t\tconst stats = await fs.stat(filePath);\n\t\treturn stats.isFile();\n\t} catch {\n\t\treturn false;\n\t}\n};","/*\n *\tHtml utilities\n */\n\n/**\n * Escape html string.\n *\n * @param value - The value.\n * @returns The escaped value.\n */\nexport const escapeHtml = (value: string): string =>\n\tvalue.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('\"', '&quot;').replaceAll(\"'\", '&#39;');\n\n/**\n *\tConvert LF and/or CR to <br>\n *\t@param text - The text to convert.\n *\t@returns The converted text.\n */\nexport const convertAsciiToHtml = (text: string): string => {\n\tlet html = escapeHtml(text);\n\n\thtml = html.replaceAll(/\\r\\n|\\r|\\n/gu, '<br />');\n\thtml = html.replaceAll('\\t', '&nbsp;&nbsp;&nbsp;');\n\n\treturn html;\n};\n\n/**\n *\tget a minimal html page.\n *\t@param body - The body.\n *\t@returns The html page.\n */\nexport const getHtmlPage = (body: string): string => `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>web_plsql error page</title>\n<style type=\"text/css\">\nhtml {\n\tfont-family: monospace, sans-serif;\n\tfont-size: 12px;\n}\nh1 {\n\tfont-size: 16px;\n\tpadding: 2px;\n\tbackground-color: #cc0000;\n}\n</style>\n</head>\n<body>\n${body}\n</body>\n</html>\n`;","/*\n *\tTrace utilities\n */\n\nimport * as rotatingFileStream from 'rotating-file-stream';\nimport type {Request} from 'express';\nimport util from 'node:util';\nimport oracledb from 'oracledb';\nimport {escapeHtml, convertAsciiToHtml} from './html.ts';\nimport {errorToString} from './errorToString.ts';\nimport {TRACE_LOG_ROTATION_SIZE, TRACE_LOG_ROTATION_INTERVAL, TRACE_LOG_MAX_ROTATED_FILES} from '../../common/constants.ts';\nimport type {environmentType} from '../types.ts';\nimport type {BindParameter, BindParameters} from 'oracledb';\n\n/**\n * Type guard for BindParameter\n * @param row - The row to check\n * @returns True if row is a BindParameter\n */\nconst isBindParameter = (row: unknown): row is BindParameter => {\n\tif (typeof row !== 'object' || row === null) {\n\t\treturn false;\n\t}\n\treturn 'dir' in row || 'type' in row || 'val' in row || 'maxSize' in row || 'maxArraySize' in row;\n};\n\ntype outputType = {\n\thtml: string;\n\ttext: string;\n};\n\nexport type messageType = {\n\ttype: 'error' | 'warning' | 'trace';\n\tmessage: string;\n\ttimestamp?: Date | null;\n\treq?: Request | null;\n\tenvironment?: environmentType | null;\n\tsql?: string | null;\n\tbind?: BindParameters | null;\n};\n\nconst SEPARATOR_H1 = '='.repeat(100);\nconst SEPARATOR_H2 = '-'.repeat(30);\n\n/**\n * Return a string representation of the value.\n *\n * @param value - Any value.\n * @param depth - Specifies the number of times to recurse while formatting object.\n * @returns The string representation.\n */\nexport const inspect = (value: unknown, depth: number | null = null): string => {\n\ttry {\n\t\treturn util.inspect(value, {showHidden: false, depth, colors: false});\n\t} catch {\n\t\t/* empty */\n\t}\n\n\ttry {\n\t\treturn JSON.stringify(value);\n\t} catch {\n\t\t/* empty */\n\t}\n\n\treturn 'Unable to convert value to string';\n};\n\n// Build text representation\n/**\n *\t@param cell - The string\n *\t@param width - The width\n *\t@returns The result\n */\nconst padCell = (cell: string, width: number) => cell.padEnd(width, ' ');\n\n/**\n * Return a tabular representation of the values.\n *\n * @param head - The header values.\n * @param body - The row values.\n * @returns The output.\n */\nexport const toTable = (head: string[], body: string[][]): outputType => {\n\tif (head.length === 0) {\n\t\tthrow new Error('head cannot be empty');\n\t}\n\n\t// Calculate column widths\n\tconst widths = head.map((h, i) => {\n\t\tconst bodyMax = Math.max(0, ...body.map((row) => (row[i] ?? '').length));\n\t\treturn Math.max(h.length, bodyMax);\n\t});\n\n\t/**\n\t *\t@param i - The index\n\t *\t@returns The width\n\t */\n\tconst getWidth = (i: number): number => widths[i] ?? 0;\n\tconst textHeader = head.map((h, i) => padCell(h, getWidth(i))).join(' | ');\n\tconst textSeparator = widths.map((w) => '-'.repeat(w ?? 0)).join('-+-');\n\tconst textRows = body.map((row) => head.map((_, i) => padCell(row[i] ?? '', getWidth(i))).join(' | '));\n\tconst text = [textHeader, textSeparator, ...textRows].join('\\n');\n\n\tconst htmlHead = head.map((h) => `<th>${escapeHtml(h)}</th>`).join('');\n\tconst htmlBody = body.map((row) => `<tr>${head.map((_, i) => `<td>${escapeHtml(row[i] ?? '')}</td>`).join('')}</tr>`).join('');\n\tconst html = `<table><thead><tr>${htmlHead}</tr></thead><tbody>${htmlBody}</tbody></table>`;\n\n\treturn {text, html};\n};\n\n/**\n * Log text to the console and to a file.\n *\n * @param text - Text to log.\n */\nexport const logToFile = (text: string): void => {\n\tconst fs = rotatingFileStream.createStream('trace.log', {\n\t\tsize: TRACE_LOG_ROTATION_SIZE, // rotate every 10 MegaBytes written\n\t\tinterval: TRACE_LOG_ROTATION_INTERVAL, // rotate daily\n\t\tmaxFiles: TRACE_LOG_MAX_ROTATED_FILES, // maximum number of rotated files to keep\n\t\tcompress: 'gzip', // compress rotated files\n\t});\n\n\tfs.write(text);\n\tfs.end();\n};\n\n/**\n * Return a string representation of the request.\n *\n * @param req - express.Request.\n * @returns The string representation.\n */\nconst inspectRequest = (req: Request): string => {\n\tconst requestData: Record<string, unknown> = {};\n\n\tObject.keys(req)\n\t\t.filter((prop) => ['originalUrl', 'params', 'query', 'url', 'method', 'body', 'files', 'secret', 'cookies'].includes(prop))\n\t\t.forEach((prop) => {\n\t\t\trequestData[prop] = req[prop as keyof Request];\n\t\t});\n\n\treturn inspect(requestData);\n};\n\n/**\n *\tReturn a string representation of the bind parameter.\n *\t@param dir - The direction.\n *\t@returns The string.\n */\nconst dirToString = (dir: number | undefined): string => {\n\tswitch (dir) {\n\t\tcase oracledb.BIND_IN:\n\t\t\treturn 'IN';\n\t\tcase oracledb.BIND_OUT:\n\t\t\treturn 'OUT';\n\t\tcase oracledb.BIND_INOUT:\n\t\t\treturn 'INOUT';\n\t\tdefault:\n\t\t\treturn '';\n\t}\n};\n\n/**\n *\tReturn a string representation of the bind type.\n *\t@param type - The type.\n *\t@returns The string.\n */\nconst bindTypeToString = (type: unknown): string => {\n\tif (typeof type === 'object' && type !== null && 'name' in type) {\n\t\treturn (type as {name: string}).name;\n\t}\n\n\tif (typeof type === 'string') {\n\t\treturn type;\n\t}\n\n\tif (typeof type === 'number') {\n\t\treturn type.toString();\n\t}\n\n\treturn '';\n};\n\n/**\n *\tReturn a string representation of the bind parameter.\n *\t@param output - The output.\n *\t@param bind - The bind parameters.\n */\nconst inspectBindParameter = (output: outputType, bind: BindParameters): void => {\n\tconst rows = Object.entries(bind);\n\n\tif (rows.length === 0) {\n\t\treturn;\n\t}\n\n\tconst body = rows.map(([id, row]) => {\n\t\tlet dir = '';\n\t\tlet maxArraySize = '';\n\t\tlet maxSize = '';\n\t\tlet bindType = '';\n\t\tlet value = '';\n\t\tlet valueType = '';\n\n\t\tif (isBindParameter(row)) {\n\t\t\tdir = dirToString(row.dir);\n\t\t\tmaxArraySize = row.maxArraySize ? row.maxArraySize.toString() : '';\n\t\t\tmaxSize = row.maxSize ? row.maxSize.toString() : '';\n\t\t\tbindType = bindTypeToString(row.type);\n\t\t\tvalue = inspect(row.val);\n\t\t\tvalueType = typeof row.val;\n\t\t} else {\n\t\t\tvalue = inspect(row);\n\t\t\tvalueType = typeof row;\n\t\t}\n\n\t\treturn [id, dir, maxArraySize, maxSize, bindType, value, valueType];\n\t});\n\n\tconst {html, text} = toTable(['id', 'dir', 'maxArraySize', 'maxSize', 'bind type', 'value', 'value type'], body);\n\n\toutput.html += html;\n\toutput.text += text;\n};\n\n/**\n * Add environment\n *\t@param output - The output.\n *\t@param environment - The environment.\n */\nconst inspectEnvironment = (output: outputType, environment: environmentType): void => {\n\tconst rows = Object.entries(environment);\n\n\tif (rows.length === 0) {\n\t\treturn;\n\t}\n\n\tconst {html, text} = toTable(['key', 'value'], rows);\n\n\toutput.html += html;\n\toutput.text += text;\n};\n\n/**\n *\tGet a block.\n *\t@param title - The name.\n *\t@param body - The name.\n *\t@returns The text.\n */\nexport const getBlock = (title: string, body: string): string => `\\n${SEPARATOR_H2}${title.toUpperCase()}${SEPARATOR_H2}\\n${body}`;\n\n/**\n *\tGet line html\n *\t@param text - The text.\n *\t@returns The line.\n */\nconst getLineHtml = (text: string): string => `<p>${convertAsciiToHtml(text)}</p>`;\n\n/**\n *\tGet line text\n *\t@param text - The text.\n *\t@returns The line.\n */\nconst getLineText = (text: string): string => `${text}\\n`;\n\n/**\n *\tAdd line\n *\t@param output - The output.\n *\t@param text - The text to convert.\n */\nconst addLine = (output: outputType, text: string): void => {\n\toutput.html += getLineHtml(text);\n\toutput.text += getLineText(text);\n};\n\n/**\n *\tAdd header\n *\t@param output - The output.\n *\t@param text - The text to convert.\n */\nconst addHeader = (output: outputType, text: string): void => {\n\toutput.html += `<h2>${text}</h2>`;\n\toutput.text += `\\n${text}\\n${'-'.repeat(text.length)}\\n`;\n};\n\n/**\n *\tAdd procedure\n *\t@param output - The output.\n *\t@param sql - The SQL to execute.\n *\t@param bind - The bind parameters.\n */\nconst addProcedure = (output: outputType, sql: string, bind: BindParameters): void => {\n\toutput.html += `${sql}<br><br>`;\n\toutput.text += `${sql}\\n\\n`;\n\n\ttry {\n\t\tinspectBindParameter(output, bind);\n\t} catch (err) {\n\t\taddLine(output, `Unable to inspect bind parameter: ${errorToString(err)}`);\n\t}\n\n\toutput.html += `<br>`;\n\toutput.text += `\\n`;\n};\n\n/**\n *\tGet a formatted message.\n *\t@param para - The req object represents the HTTP request.\n *\t@returns The output.\n */\nexport const getFormattedMessage = (para: messageType): outputType => {\n\tconst timestamp = para.timestamp ?? new Date();\n\n\t// header\n\tconst url = typeof para.req?.originalUrl === 'string' && para.req.originalUrl.length > 0 ? ` on ${para.req.originalUrl}` : '';\n\tconst type = (para.type ?? 'trace').toUpperCase();\n\tconst header = `${type} at ${timestamp.toUTCString()}${url}`;\n\tconst output: outputType = {\n\t\thtml: `<h1>${header}</h1>`,\n\t\ttext: `\\n\\n${SEPARATOR_H1}\\n== ${header}\\n${SEPARATOR_H1}\\n`,\n\t};\n\n\t// error\n\taddHeader(output, 'ERROR');\n\taddLine(output, para.message);\n\n\t// request\n\tif (para.req) {\n\t\taddHeader(output, 'REQUEST');\n\t\taddLine(output, inspectRequest(para.req));\n\t}\n\n\t// parameters\n\tif (para.sql && para.bind) {\n\t\taddHeader(output, 'PROCEDURE');\n\t\taddProcedure(output, para.sql, para.bind);\n\t}\n\n\t// environment\n\tif (para.environment) {\n\t\taddHeader(output, 'ENVIRONMENT');\n\t\tinspectEnvironment(output, para.environment);\n\t}\n\n\treturn output;\n};\n\n/**\n *\tLog a warning message.\n *\t@param para - The req object represents the HTTP request.\n */\nexport const warningMessage = (para: messageType): void => {\n\tconst {text} = getFormattedMessage(para);\n\n\t// trace to file\n\tlogToFile(text);\n\n\t// console\n\tconsole.warn(text);\n};","import {inspect} from './trace.ts';\n\n/**\n * Convert Error to a string.\n *\n * @param error - The error.\n * @returns The string representation.\n */\nexport const errorToString = (error: unknown): string => {\n\tif (typeof error === 'string') {\n\t\treturn error;\n\t} else if (error instanceof Error) {\n\t\tconst parts = [error.name];\n\t\tif (typeof error.message === 'string' && error.message.length > 0) {\n\t\t\tparts.push(error.message);\n\t\t}\n\t\tif (typeof error.stack === 'string' && error.stack.length > 0) {\n\t\t\tparts.push(error.stack);\n\t\t}\n\t\treturn parts.join('\\n');\n\t} else {\n\t\treturn inspect(error);\n\t}\n};","/*\n *\tProcess file uploads\n */\n\nimport debugModule from 'debug';\nconst debug = debugModule('webplsql:fileUpload');\n\nimport {BUFFER} from '../../util/oracledb-provider.ts';\nimport {readFile, removeFile} from '../../util/file.ts';\nimport {errorToString} from '../../util/errorToString.ts';\nimport type {Connection} from 'oracledb';\nimport z from 'zod';\nimport type {Request} from 'express';\nimport type {fileUploadType} from '../../types.ts';\n\nconst z$reqFiles = z.array(\n\tz.strictObject({\n\t\tfieldname: z.string(),\n\t\toriginalname: z.string(),\n\t\tencoding: z.string(),\n\t\tmimetype: z.string(),\n\t\tdestination: z.string(),\n\t\tfilename: z.string(),\n\t\tpath: z.string(),\n\t\tsize: z.number(),\n\t}),\n);\n\n/**\n * Get the files\n *\n * @param req - The req object represents the HTTP request.\n * @returns Promise that resolves with an array of files to be uploaded.\n */\nexport const getFiles = (req: Request): fileUploadType[] => {\n\tif (!('files' in req)) {\n\t\tdebug('getFiles: no files');\n\t\treturn [];\n\t}\n\n\tif (typeof req.files === 'object' && req.files !== null && Object.keys(req.files as object).length === 0) {\n\t\tdebug('getFiles: no files');\n\t\treturn [];\n\t}\n\n\tconst files = z$reqFiles.parse(req.files) as fileUploadType[];\n\n\tfor (const file of files) {\n\t\tfile.filename += `/${file.originalname}`;\n\t}\n\n\tdebug('getFiles', files);\n\n\treturn files;\n};\n\n/**\n * Upload the given file and return a promise.\n *\n * @param file - The file to upload.\n * @param doctable - The file to upload.\n * @param databaseConnection - The file to upload.\n * @returns Promise that resolves when uploaded.\n */\nexport const uploadFile = async (file: fileUploadType, doctable: string, databaseConnection: Connection): Promise<void> => {\n\tdebug(`uploadFile`, file, doctable);\n\n\t/* v8 ignore next - defensive validation */\n\tif (typeof doctable !== 'string' || doctable.length === 0) {\n\t\tthrow new Error(`Unable to upload file \"${file.filename}\" because the option \"\"doctable\" has not been defined`);\n\t}\n\n\t// read file\n\tlet blobContent;\n\ttry {\n\t\tblobContent = await readFile(file.path);\n\t} catch (err) {\n\t\tthrow new Error(`Unable to load file \"${file.path}\".\\n${errorToString(err)}`);\n\t}\n\n\t// insert file in document table\n\tconst sql = `INSERT INTO ${doctable} (name, mime_type, doc_size, dad_charset, last_updated, content_type, blob_content) VALUES (:name, :mime_type, :doc_size, 'ascii', SYSDATE, 'BLOB', :blob_content)`;\n\tconst bind = {\n\t\tname: file.filename,\n\t\tmime_type: file.mimetype,\n\t\tdoc_size: file.size,\n\t\tblob_content: {\n\t\t\tval: blobContent,\n\t\t\ttype: BUFFER,\n\t\t},\n\t};\n\ttry {\n\t\tawait databaseConnection.execute(sql, bind, {autoCommit: true});\n\t} catch (err) {\n\t\tthrow new Error(`Unable to insert file \"${file.filename}\".\\n${errorToString(err)}`);\n\t}\n\n\t// remove the file\n\ttry {\n\t\tawait removeFile(file.path);\n\t} catch (err) {\n\t\tthrow new Error(`Unable to remove file \"${file.filename}\".\\n${errorToString(err)}`);\n\t}\n};","/*\n *\tInvoke the Oracle procedure and return the raw content of the page\n */\n\nimport debugModule from 'debug';\nconst debug = debugModule('webplsql:procedureVariable');\n\nimport {BIND_IN, STRING} from '../../util/oracledb-provider.ts';\nimport type {Request} from 'express';\nimport type {argObjType} from '../../types.ts';\nimport type {BindParameters} from 'oracledb';\n\n/**\n *\tGet the sql statement and bindings for the procedure to execute for a variable number of arguments\n *\t@param _req - The req object represents the HTTP request. (only used for debugging)\n *\t@param procName - The procedure to execute\n *\t@param argObj - The arguments to pass to the procedure\n *\t@returns The SQL statement and bindings for the procedure to execute\n */\nexport const getProcedureVariable = (_req: Request, procName: string, argObj: argObjType): {sql: string; bind: BindParameters} => {\n\t/* v8 ignore start */\n\tif (debug.enabled) {\n\t\tdebug(`getProcedureVariable: ${procName} arguments=`, argObj);\n\t}\n\t/* v8 ignore stop */\n\n\tconst names: string[] = [];\n\tconst values: string[] = [];\n\n\tfor (const key in argObj) {\n\t\tconst value = argObj[key];\n\t\tif (typeof value === 'string') {\n\t\t\tnames.push(key);\n\t\t\tvalues.push(value);\n\t\t} else if (Array.isArray(value)) {\n\t\t\tvalue.forEach((item) => {\n\t\t\t\tnames.push(key);\n\t\t\t\tvalues.push(item);\n\t\t\t});\n\t\t}\n\t}\n\n\treturn {\n\t\tsql: `${procName}(:argnames, :argvalues)`,\n\t\tbind: {\n\t\t\targnames: {dir: BIND_IN, type: STRING, val: names},\n\t\t\targvalues: {dir: BIND_IN, type: STRING, val: values},\n\t\t},\n\t};\n};","/*\n *\tRequestError\n */\n\nexport class RequestError extends Error {\n\ttimestamp: Date;\n\n\t/**\n\t * @param message - The error message.\n\t */\n\tconstructor(message: string) {\n\t\tsuper(message);\n\n\t\t// Maintains proper stack trace for where our error was thrown (only available on V8)\n\t\tif (Error.captureStackTrace) {\n\t\t\tError.captureStackTrace(this, RequestError);\n\t\t}\n\n\t\t// Custom debugging information\n\t\tthis.timestamp = new Date();\n\t}\n}","/**\n * Get duration as human readable string.\n * @param duration - Milliseconds.\n * @returns String.\n */\nexport const humanDuration = (duration: number): string => {\n\tif (!Number.isFinite(duration)) return 'invalid';\n\n\tconst ms = Math.floor(duration % 1000);\n\tconst s = Math.floor((duration / 1000) % 60);\n\tconst m = Math.floor((duration / (1000 * 60)) % 60);\n\tconst h = Math.floor((duration / (1000 * 60 * 60)) % 24);\n\tconst d = Math.floor(duration / (1000 * 60 * 60 * 24));\n\n\tconst parts = [];\n\tif (d) parts.push(`${d}d`);\n\tif (h) parts.push(`${h}h`);\n\tif (m) parts.push(`${m}m`);\n\tif (s) parts.push(`${s}s`);\n\tif (ms || parts.length === 0) parts.push(`${ms}ms`);\n\n\treturn parts.join(' ');\n};\n\n/**\n * Convert a string to a number\n *\n * @param value - The string to convert\n * @returns The number or null if the string could not be converted\n */\nexport const stringToNumber = (value: unknown): number | null => {\n\t// is the value already of type number?\n\tif (typeof value === 'number') {\n\t\treturn !Number.isNaN(value) && Number.isFinite(value) ? value : null;\n\t}\n\n\t// Test for invalid characters\n\t// oxlint-disable-next-line unicorn/better-regex\n\tif (typeof value !== 'string' || !/^[+-]?(?:\\d+(?:\\.\\d+)?|\\.\\d+)(?:E[+-]?\\d+)?$/iu.test(value)) {\n\t\treturn null;\n\t}\n\n\t// Convert value to a number\n\treturn Number(value);\n};\n\n/**\n * Convert a string to a integer\n *\n * @param value - The value to convert\n * @returns The integer or null if the string could not be converted\n */\nexport const stringToInteger = (value: unknown): number | null => {\n\t// is the value already a \"real\" integer, we just return the value\n\tif (typeof value === 'number' && Number.isInteger(value)) {\n\t\treturn value;\n\t}\n\n\t// try to convert value to a number\n\tconst num = stringToNumber(value);\n\tif (num === null || !Number.isInteger(num)) {\n\t\treturn null;\n\t}\n\n\treturn num;\n};\n\n/**\n * Centers text within a fixed width by padding both sides with spaces.\n * If padding is odd, the extra space goes to the right.\n *\n * @param text - The string to center\n * @param width - Total output width in characters\n * @returns Padded string of exactly `width` characters\n * @throws {RangeError} If `width` is less than `text.length`\n */\nexport const centerText = (text: string, width: number, padding = ' '): string => {\n\tif (width < text.length) {\n\t\tthrow new RangeError(`width (${width}) < text.length (${text.length})`);\n\t}\n\n\tconst total = width - text.length;\n\tconst left = Math.floor(total / 2);\n\tconst right = total - left;\n\n\treturn padding.repeat(left) + text + padding.repeat(right);\n};","/*\n *\tInvoke the Oracle procedure and return the raw content of the page\n */\n\nimport debugModule from 'debug';\nconst debug = debugModule('webplsql:procedureNamed');\n\nimport {BIND_IN, BIND_OUT, STRING, DB_TYPE_VARCHAR, DB_TYPE_CLOB, DB_TYPE_NUMBER, DB_TYPE_DATE} from '../../util/oracledb-provider.ts';\nimport z from 'zod';\nimport {RequestError} from './requestError.ts';\nimport {errorToString} from '../../util/errorToString.ts';\nimport {stringToNumber} from '../../util/util.ts';\nimport {toTable, warningMessage, inspect} from '../../util/trace.ts';\nimport {MAX_PROCEDURE_PARAMETERS} from '../../../common/constants.ts';\nimport type {Request} from 'express';\nimport type {Connection, Result, BindParameter, BindParameters} from 'oracledb';\nimport type {argObjType, argsType, ArgumentCache} from '../../types.ts';\n\nconst SQL_GET_ARGUMENT = [\n\t'DECLARE',\n\t'\tschemaName\t\tVARCHAR2(32767);',\n\t'\tpart1\t\t\tVARCHAR2(32767);',\n\t'\tpart2\t\t\tVARCHAR2(32767);',\n\t'\tdblink\t\t\tVARCHAR2(32767);',\n\t'\tobjectType\t\tNUMBER;',\n\t'\tobjectID\t\tNUMBER;',\n\t'BEGIN',\n\t'\tdbms_utility.name_resolve(name=>UPPER(:name), context=>1, schema=>schemaName, part1=>part1, part2=>part2, dblink=>dblink, part1_type=>objectType, object_number=>objectID);',\n\t'\tIF (part1 IS NOT NULL) THEN',\n\t'\t\tSELECT argument_name, data_type BULK COLLECT INTO :names, :types FROM all_arguments WHERE owner = schemaName AND package_name = part1 AND object_name = part2 AND argument_name IS NOT NULL ORDER BY overload, sequence;',\n\t'\tELSE',\n\t'\t\tSELECT argument_name, data_type BULK COLLECT INTO :names, :types FROM all_arguments WHERE owner = schemaName AND package_name IS NULL AND object_name = part2 AND argument_name IS NOT NULL ORDER BY overload, sequence;',\n\t'\tEND IF;',\n\t'END;',\n].join('\\n');\n\nconst DATA_TYPES = Object.freeze({\n\tVARCHAR2: 'VARCHAR2',\n\tCHAR: 'CHAR',\n\tBINARY_INTEGER: 'BINARY_INTEGER',\n\tNUMBER: 'NUMBER',\n\tDATE: 'DATE',\n\tCLOB: 'CLOB',\n\tPL_SQL_TABLE: 'PL/SQL TABLE',\n\t//\tPL/SQL BOOLEAN\n\t//\tPL/SQL RECORD\n\t//\tOBJECT\n\t//\tTABLE\n\t//\tBLOB\n\t//\tRAW\n\t//\tVARRAY\n\t//\tREF CURSOR\n});\n\n/**\n *\tRetrieve the argument types for a given procedure to be executed.\n *\tThis is important because if the procedure is defined to take a PL/SQL indexed table,\n *\twe must provise a table, even if there is only one argument to be submitted.\n *\t@param procedure - The procedure\n *\t@param databaseConnection - The database connection\n *\t@returns The argument types\n */\nconst loadArguments = async (procedure: string, databaseConnection: Connection): Promise<argsType> => {\n\tconst bind: BindParameters = {\n\t\tname: {dir: BIND_IN, type: STRING, val: procedure},\n\t\tnames: {dir: BIND_OUT, type: STRING, maxSize: 60, maxArraySize: MAX_PROCEDURE_PARAMETERS},\n\t\ttypes: {dir: BIND_OUT, type: STRING, maxSize: 60, maxArraySize: MAX_PROCEDURE_PARAMETERS},\n\t};\n\n\tlet result: Result<unknown> = {};\n\ttry {\n\t\tresult = await databaseConnection.execute(SQL_GET_ARGUMENT, bind);\n\t} catch (err) {\n\t\t/* v8 ignore start */\n\t\tdebug('result', result);\n\t\t/* v8 ignore stop */\n\n\t\tconst message = `Error when retrieving arguments\\n${SQL_GET_ARGUMENT}\\n${errorToString(err)}`;\n\t\tthrow new RequestError(message);\n\t}\n\n\tlet data: {names: (string | null)[]; types: (string | null)[]};\n\ttry {\n\t\tdata = z\n\t\t\t.object({\n\t\t\t\tnames: z.array(z.string().nullable()),\n\t\t\t\ttypes: z.array(z.string().nullable()),\n\t\t\t})\n\t\t\t.parse(result.outBinds);\n\t} catch (err) {\n\t\t/* v8 ignore start */\n\t\tdebug('result.outBinds', result.outBinds);\n\t\t/* v8 ignore stop */\n\n\t\tconst message = `Error when decoding arguments\\n${SQL_GET_ARGUMENT}\\n${errorToString(err)}`;\n\t\tthrow new RequestError(message);\n\t}\n\n\tif (data.names.length !== data.types.length) {\n\t\tthrow new RequestError('Error when decoding arguments. The number of names and types does not match');\n\t}\n\n\tconst argTypes: argsType = {};\n\tfor (let i = 0; i < data.names.length; i++) {\n\t\tconst name = data.names[i];\n\t\tconst type = data.types[i];\n\t\tif (name && type) {\n\t\t\targTypes[name.toLowerCase()] = type;\n\t\t}\n\t}\n\n\treturn argTypes;\n};\n\n/**\n *\tFind the argument types for a given procedure to be executed.\n *\tAs the arguments are cached, we first look up the cache and only if not yet available we load them.\n *\t@param procedure - The procedure\n *\t@param databaseConnection - The database connection\n *\t@param argumentCache - The argument cache.\n *\t@returns The argument types\n */\nconst findArguments = async (procedure: string, databaseConnection: Connection, argumentCache: ArgumentCache): Promise<argsType> => {\n\tconst key = procedure.toUpperCase();\n\n\t// lookup in the cache\n\tconst cachedArgs = argumentCache.get(key);\n\n\t// if we found the procedure in the cache, we return it\n\tif (cachedArgs) {\n\t\t/* v8 ignore start */\n\t\tif (debug.enabled) {\n\t\t\tdebug(`findArguments: procedure \"${procedure}\" found in cache`);\n\t\t}\n\t\t/* v8 ignore stop */\n\n\t\treturn cachedArgs;\n\t}\n\n\t// load from database\n\t/* v8 ignore start */\n\tif (debug.enabled) {\n\t\tdebug(`findArguments: procedure \"${procedure}\" not found in cache and must be loaded`);\n\t}\n\t/* v8 ignore stop */\n\n\tconst args = await loadArguments(procedure, databaseConnection);\n\n\t// add to the cache\n\targumentCache.set(key, args);\n\n\treturn args;\n};\n\n/**\n *\tGet the bindling for an argument.\n *\t@param argName - The argument name.\n *\t@param argValue - The argument value.\n *\t@param argType - The argument type.\n *\t@returns The binding.\n */\nexport const getBinding = (argName: string, argValue: unknown, argType: string): BindParameter => {\n\tconst isEmpty = argValue === '' || argValue === null || argValue === undefined;\n\n\tif (argType === DATA_TYPES.VARCHAR2 || argType === DATA_TYPES.CHAR) {\n\t\treturn {dir: BIND_IN, type: DB_TYPE_VARCHAR, val: isEmpty ? null : argValue};\n\t}\n\n\tif (argType === DATA_TYPES.CLOB) {\n\t\treturn {dir: BIND_IN, type: DB_TYPE_CLOB, val: isEmpty ? null : argValue};\n\t}\n\n\tif (argType === DATA_TYPES.NUMBER || argType === DATA_TYPES.BINARY_INTEGER) {\n\t\tif (isEmpty) {\n\t\t\treturn {dir: BIND_IN, type: DB_TYPE_NUMBER, val: null};\n\t\t}\n\t\tconst value = stringToNumber(argValue);\n\t\tif (value === null) {\n\t\t\tthrow new Error(`Error in named parameter \"${argName}\": invalid value \"${inspect(argValue)}\" for type \"${argType}\"`);\n\t\t}\n\n\t\treturn {dir: BIND_IN, type: DB_TYPE_NUMBER, val: value};\n\t}\n\n\tif (argType === DATA_TYPES.DATE) {\n\t\tif (isEmpty) {\n\t\t\treturn {dir: BIND_IN, type: DB_TYPE_DATE, val: null};\n\t\t}\n\t\tif (typeof argValue !== 'string') {\n\t\t\tthrow new TypeError(`Error in named parameter \"${argName}\": invalid value \"${inspect(argValue)}\" for type \"${argType}\"`);\n\t\t}\n\t\tconst value = new Date(argValue);\n\t\tif (Number.isNaN(value.getTime())) {\n\t\t\tthrow new TypeError(`Error in named parameter \"${argName}\": invalid value \"${inspect(argValue)}\" for type \"${argType}\"`);\n\t\t}\n\n\t\treturn {dir: BIND_IN, type: DB_TYPE_DATE, val: value};\n\t}\n\n\tif (argType === DATA_TYPES.PL_SQL_TABLE || Array.isArray(argValue)) {\n\t\tconst value = typeof argValue === 'string' ? [argValue] : argValue;\n\n\t\treturn {dir: BIND_IN, type: DB_TYPE_VARCHAR, val: value};\n\t}\n\n\tthrow new Error(`Error in named parameter \"${argName}\": invalid binding type \"${argType}\"`);\n};\n\n/**\n *\tGet binding table for tracing.\n *\t@param argObj - The arguments to pass to the procedure\n *\t@param argTypes - The argument types.\n *\t@returns The text.\n */\nconst inspectBindings = (argObj: argObjType, argTypes: argsType): string => {\n\tconst rows = Object.entries(argObj).map(([key, value]) => {\n\t\treturn [key, value.toString(), typeof value, argTypes[key.toLowerCase()] ?? 'unknown'];\n\t});\n\n\tconst {text} = toTable(['id', 'value', 'value type', 'argument type'], rows);\n\n\treturn text;\n};\n\n/**\n *\tGet the sql statement and bindings for the procedure to execute for a fixed number of arguments\n *\t@param req - The req object represents the HTTP request. (only used for debugging)\n *\t@param procName - The procedure to execute\n *\t@param argObj - The arguments to pass to the procedure\n *\t@param databaseConnection - The database connection\n *\t@param argumentCache - The argument cache.\n *\t@returns The SQL statement and bindings for the procedure to execute\n */\nexport const getProcedureNamed = async (\n\treq: Request,\n\tprocName: string,\n\targObj: argObjType,\n\tdatabaseConnection: Connection,\n\targumentCache: ArgumentCache,\n): Promise<{sql: string; bind: BindParameters}> => {\n\tdebug(`getProcedureNamed: ${procName} arguments=`, argObj);\n\n\t// get the types of the arguments\n\tconst argTypes = await findArguments(procName, databaseConnection, argumentCache);\n\n\tconst sqlParameter: string[] = [];\n\n\tconst bindings: BindParameters = {};\n\n\t// bindings for the statement\n\tfor (const key in argObj) {\n\t\tconst parameterName = `p_${key}`;\n\t\tconst argValue = argObj[key];\n\t\tconst argType = argTypes[key.toLowerCase()];\n\t\tlet bind: BindParameter = {dir: BIND_IN, type: DB_TYPE_VARCHAR, val: argValue};\n\n\t\tif (argType) {\n\t\t\tbind = getBinding(key, argValue, argType);\n\t\t} else {\n\t\t\tconst text = inspectBindings(argObj, argTypes);\n\t\t\twarningMessage({\n\t\t\t\ttype: 'warning',\n\t\t\t\tmessage: `Error in named parameter \"${key}\": invalid binding type \"${argType}\"\\n\\n${text}`,\n\t\t\t\treq,\n\t\t\t});\n\t\t}\n\n\t\tsqlParameter.push(`${key}=>:${parameterName}`);\n\t\tbindings[parameterName] = bind;\n\t}\n\n\t// select statement\n\tconst sql = `${procName}(${sqlParameter.join(', ')})`;\n\n\t/* v8 ignore start */\n\tif (debug.enabled) {\n\t\tdebug(sql);\n\t\tdebug(inspectBindings(argObj, argTypes));\n\t}\n\t/* v8 ignore stop */\n\n\treturn {sql, bind: bindings};\n};","import debugModule from 'debug';\nconst debug = debugModule('webplsql:parsePage');\n\nimport type {pageType, cookieType} from '../../types.ts';\n\n/**\n *\tParse the header and split it up into the individual components\n *\n * @param text - The text returned from the PL/SQL procedure.\n * @returns The parsed page.\n */\nexport const parsePage = (text: string): pageType => {\n\tconst page: pageType = {\n\t\tbody: '',\n\t\thead: {\n\t\t\tcookies: [],\n\t\t\totherHeaders: {},\n\t\t},\n\t\tfile: {\n\t\t\tfileType: null,\n\t\t\tfileSize: null,\n\t\t\tfileBlob: null,\n\t\t},\n\t};\n\n\t//\n\t//\t1)\tSplit up the text in header and body\n\t//\n\n\t// Find the end of the header identified by \\n\\n\n\tlet head = '';\n\tconst headerEndPosition = text.indexOf('\\n\\n');\n\tif (headerEndPosition === -1) {\n\t\thead = text;\n\t} else {\n\t\thead = text.slice(0, Math.max(0, headerEndPosition + 2));\n\t\tpage.body = text.slice(Math.max(0, headerEndPosition + 2));\n\t}\n\n\t//\n\t//\t2)\tparse the headers\n\t//\n\n\tdebug('parsing the headers received from PL/SQL');\n\thead.split('\\n').forEach((line) => {\n\t\tconst header = getHeader(line);\n\n\t\tif (header) {\n\t\t\tswitch (header.name.toLowerCase()) {\n\t\t\t\tcase 'set-cookie':\n\t\t\t\t\t{\n\t\t\t\t\t\tconst cookie = parseCookie(header.value);\n\t\t\t\t\t\tdebug(`oracle header \"set-cookie\" with value \"${header.value}\" was received has been parsed to ${JSON.stringify(cookie)}`);\n\t\t\t\t\t\tif (cookie === null) {\n\t\t\t\t\t\t\tthrow new Error(`Unable to parse header \"set-cookie\" with value \"${header.value}\" received from PL/SQL`);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tpage.head.cookies.push(cookie);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'content-type':\n\t\t\t\t\tpage.head.contentType = header.value;\n\t\t\t\t\tdebug(`oracle header \"content-type\" with value \"${page.head.contentType}\" was parsed`);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'x-db-content-length':\n\t\t\t\t\t{\n\t\t\t\t\t\tconst contentLength = Number.parseInt(header.value, 10);\n\t\t\t\t\t\tif (Number.isNaN(contentLength)) {\n\t\t\t\t\t\t\tthrow new TypeError(`Unable to parse header \"x-db-content-length\" with value \"${header.value}\" received from PL/SQL`);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tpage.head.contentLength = contentLength;\n\t\t\t\t\t\t\tdebug(`oracle header \"x-db-content-length\" with value \"${page.head.contentLength}\" was parsed`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'status':\n\t\t\t\t\t{\n\t\t\t\t\t\tconst statusCode = Number.parseInt(header.value, 10);\n\t\t\t\t\t\tif (Number.isNaN(statusCode)) {\n\t\t\t\t\t\t\tthrow new TypeError(`Unable to parse header \"status\" with value \"${header.value}\" received from PL/SQL`);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tpage.head.statusCode = statusCode;\n\t\t\t\t\t\t\tdebug(`oracle header \"status\" with value \"${page.head.statusCode}\" was parsed`);\n\t\t\t\t\t\t\tconst index = header.value.indexOf(' ');\n\t\t\t\t\t\t\tif (index !== -1) {\n\t\t\t\t\t\t\t\tpage.head.statusDescription = header.value.slice(Math.max(0, index + 1));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'location':\n\t\t\t\t\tpage.head.redirectLocation = header.value;\n\t\t\t\t\tdebug(`oracle header \"location\" with value \"${page.head.statusCode}\" was parsed`);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'x-oracle-ignore':\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tpage.head.otherHeaders[header.name] = header.value;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t});\n\n\treturn page;\n};\n\n/**\n *\tGet a header line\n *\t@param line - The line\n *\t@returns The header.\n */\nconst getHeader = (line: string): {name: string; value: string} | null => {\n\tconst index = line.indexOf(':');\n\n\tif (index !== -1) {\n\t\treturn {\n\t\t\tname: line.slice(0, Math.max(0, index)).trim(),\n\t\t\tvalue: line.slice(Math.max(0, index + 1)).trim(),\n\t\t};\n\t}\n\n\treturn null;\n};\n\n/**\n *\tParses a cookie string\n *\t@param text - The cookie string.\n *\t@returns The parsed cookie.\n */\nconst parseCookie = (text: string): cookieType | null => {\n\t// validate\n\tif (typeof text !== 'string' || text.trim().length === 0) {\n\t\treturn null;\n\t}\n\n\t// split the cookie into it's parts\n\tlet cookieElements = text.split(';');\n\n\t// trim cookie elements\n\tcookieElements = cookieElements.map((element) => element.trim());\n\n\t// get name and value\n\tconst firstElement = cookieElements[0];\n\tif (!firstElement) {\n\t\treturn null;\n\t}\n\tconst index = firstElement.indexOf('=');\n\tif (index <= 0) {\n\t\t// if the index is -1, there is no equal sign and if it's 0 the name is empty\n\t\treturn null;\n\t}\n\n\tconst cookie: cookieType = {\n\t\tname: firstElement.slice(0, Math.max(0, index)).trim(),\n\t\tvalue: firstElement.slice(Math.max(0, index + 1)).trim(),\n\t\toptions: {},\n\t};\n\n\t// remove the first element\n\tcookieElements.shift();\n\n\t// get the other options\n\tcookieElements.forEach((element) => {\n\t\tif (element.toLowerCase().startsWith('path=')) {\n\t\t\tcookie.options.path = element.slice(5);\n\t\t} else if (element.toLowerCase().startsWith('domain=')) {\n\t\t\tcookie.options.domain = element.slice(7);\n\t\t} else if (element.toLowerCase().startsWith('secure')) {\n\t\t\tcookie.options.secure = true;\n\t\t} else if (element.toLowerCase().startsWith('expires=')) {\n\t\t\tconst date = tryDecodeDate(element.slice(8));\n\t\t\tif (date) {\n\t\t\t\tcookie.options.expires = date;\n\t\t\t}\n\t\t} else if (element.toLowerCase().startsWith('httponly')) {\n\t\t\tcookie.options.httpOnly = true;\n\t\t}\n\t});\n\n\treturn cookie;\n};\n\n/**\n * Try to decode a date\n * @param value - The value to decode.\n * @returns The decoded date or null.\n */\nconst tryDecodeDate = (value: string): Date | null => {\n\tconst result = new Date(value);\n\tif (Number.isNaN(result.getTime())) {\n\t\treturn null;\n\t}\n\treturn result;\n};","/*\n *\tPage the raw page content and return the content to the client\n */\n\nimport debugModule from 'debug';\nconst debug = debugModule('webplsql:sendResponse');\n\nimport stream from 'node:stream';\nimport {getBlock} from '../../util/trace.ts';\nimport type {Request, Response} from 'express';\nimport type {pageType} from '../../types.ts';\n\n/**\n * Pipe a readable to the response and await completion.\n * Listeners are attached before piping to avoid races.\n *\n * @param readable - The source readable stream.\n * @param res - The HTTP response stream.\n */\nconst pipeReadableToResponse = async (readable: stream.Readable, res: Response): Promise<void> => {\n\tawait new Promise<void>((resolve, reject) => {\n\t\tlet settled = false;\n\t\tconst canAddResponseListener = typeof res.on === 'function';\n\t\tconst canRemoveResponseListener = typeof res.removeListener === 'function';\n\n\t\tconst cleanup = () => {\n\t\t\treadable.removeListener('end', onReadableEnd);\n\t\t\treadable.removeListener('close', onReadableClose);\n\t\t\treadable.removeListener('error', onReadableError);\n\t\t\tif (canRemoveResponseListener) {\n\t\t\t\tres.removeListener('finish', onResponseFinish);\n\t\t\t\tres.removeListener('close', onResponseClose);\n\t\t\t}\n\t\t};\n\n\t\tconst resolveOnce = () => {\n\t\t\tif (settled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\tresolve();\n\t\t};\n\n\t\tconst rejectOnce = (error: Error) => {\n\t\t\tif (settled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\treject(error);\n\t\t};\n\n\t\tconst onReadableEnd = () => {\n\t\t\tresolveOnce();\n\t\t};\n\n\t\tconst onReadableClose = () => {\n\t\t\tresolveOnce();\n\t\t};\n\n\t\tconst onReadableError = (error: Error) => {\n\t\t\trejectOnce(error);\n\t\t};\n\n\t\tconst onResponseFinish = () => {\n\t\t\tresolveOnce();\n\t\t};\n\n\t\tconst onResponseClose = () => {\n\t\t\tresolveOnce();\n\t\t};\n\n\t\treadable.on('end', onReadableEnd);\n\t\treadable.on('close', onReadableClose);\n\t\treadable.on('error', onReadableError);\n\t\tif (canAddResponseListener) {\n\t\t\tres.on('finish', onResponseFinish);\n\t\t\tres.on('close', onResponseClose);\n\t\t}\n\n\t\ttry {\n\t\t\treadable.pipe(res);\n\t\t} catch (error: unknown) {\n\t\t\trejectOnce(error instanceof Error ? error : new Error(String(error)));\n\t\t}\n\t});\n};\n\n/**\n *\tSend \"default\" response to the browser\n *\t@param _req - The req object represents the HTTP request.\n *\t@param res - The res object represents the HTTP response that an Express app sends when it gets an HTTP request.\n *\t@param page - The page to send.\n */\nexport const sendResponse = async (_req: Request, res: Response, page: pageType): Promise<void> => {\n\tconst debugText: string[] = [];\n\n\t// Send the \"cookies\"\n\tpage.head.cookies.forEach((cookie) => {\n\t\t/* v8 ignore start */\n\t\tif (debug.enabled) {\n\t\t\tdebugText.push(`res.cookie: name=\"${cookie.name}\" value=\"${cookie.value}\" options=${JSON.stringify(cookie.options)}`);\n\t\t}\n\t\t/* v8 ignore stop */\n\n\t\tres.cookie(cookie.name, cookie.value, cookie.options);\n\t});\n\n\t// If there is a \"redirectLocation\" header, we immediately redirect and return\n\tif (typeof page.head.redirectLocation === 'string' && page.head.redirectLocation.length > 0) {\n\t\t/* v8 ignore start */\n\t\tif (debug.enabled) {\n\t\t\tdebugText.push(`res.redirect(302, \"${page.head.redirectLocation}\")`);\n\t\t\tdebug(getBlock('RESPONSE', debugText.join('\\n')));\n\t\t}\n\t\t/* v8 ignore stop */\n\n\t\tres.redirect(302, page.head.redirectLocation);\n\t\treturn;\n\t}\n\n\t// Send all the \"otherHeaders\"\n\tfor (const key in page.head.otherHeaders) {\n\t\t/* v8 ignore start */\n\t\tif (debug.enabled) {\n\t\t\tdebugText.push(`res.set(\"${key}\", \"${page.head.otherHeaders[key]}\")`);\n\t\t}\n\t\t/* v8 ignore stop */\n\n\t\tres.set(key, page.head.otherHeaders[key]);\n\t}\n\n\t// If this is a file download, we eventually set the \"Content-Type\" and the file content and then return.\n\tif (page.file.fileType === 'B' || page.file.fileType === 'F') {\n\t\tconst headers: Record<string, string> = {};\n\n\t\tif (typeof page.head.contentType === 'string' && page.head.contentType.length > 0) {\n\t\t\theaders['Content-Type'] = page.head.contentType;\n\t\t}\n\n\t\tif (typeof page.file.fileSize === 'number' && page.file.fileSize > 0) {\n\t\t\theaders['Content-Length'] = page.file.fileSize.toString();\n\t\t}\n\n\t\tif (Object.keys(headers).length > 0) {\n\t\t\t/* v8 ignore start */\n\t\t\tif (debug.enabled) {\n\t\t\t\tdebugText.push(`res.writeHead(200, ${JSON.stringify(headers)})`);\n\t\t\t}\n\t\t\t/* v8 ignore stop */\n\n\t\t\tres.writeHead(200, headers);\n\t\t}\n\n\t\t// Check if fileBlob is a stream\n\t\tif (page.file.fileBlob instanceof stream.Readable) {\n\t\t\t/* v8 ignore start */\n\t\t\tif (debug.enabled) {\n\t\t\t\tdebugText.push(`res.pipe(\"${page.file.fileType ?? ''}\") - streaming`);\n\t\t\t\tdebug(getBlock('RESPONSE', debugText.join('\\n')));\n\t\t\t}\n\t\t\t/* v8 ignore stop */\n\n\t\t\tawait pipeReadableToResponse(page.file.fileBlob, res);\n\t\t} else {\n\t\t\t/* v8 ignore start */\n\t\t\tif (debug.enabled) {\n\t\t\t\tdebugText.push(`res.end(\"${page.file.fileType ?? ''}\") - buffer`);\n\t\t\t\tdebug(getBlock('RESPONSE', debugText.join('\\n')));\n\t\t\t}\n\t\t\t/* v8 ignore stop */\n\n\t\t\tres.end(page.file.fileBlob, 'binary');\n\t\t}\n\t\treturn;\n\t}\n\n\t// Is the a \"contentType\" header\n\tif (typeof page.head.contentType === 'string' && page.head.contentType.length > 0) {\n\t\t/* v8 ignore start */\n\t\tif (debug.enabled) {\n\t\t\tdebugText.push(`res.set(\"Content-Type\", \"${page.head.contentType}\")`);\n\t\t}\n\t\t/* v8 ignore stop */\n\n\t\tres.set('Content-Type', page.head.contentType);\n\t}\n\n\t// If we have a \"Status\" header, we send the header and then return.\n\tif (typeof page.head.statusCode === 'number') {\n\t\t/* v8 ignore start */\n\t\tif (debug.enabled) {\n\t\t\tdebugText.push(`res.status(page.head.statusCode).send(\"${page.head.statusDescription ?? ''}\")`);\n\t\t\tdebug(getBlock('RESPONSE', debugText.join('\\n')));\n\t\t}\n\t\t/* v8 ignore stop */\n\n\t\tres.status(page.head.statusCode).send(page.head.statusDescription);\n\t\treturn;\n\t}\n\n\t// Send the body\n\t/* v8 ignore start */\n\tif (debug.enabled) {\n\t\tif (page.body instanceof stream.Readable) {\n\t\t\tdebugText.push(`${'-'.repeat(60)}\\n[Stream Body]`);\n\t\t} else {\n\t\t\tdebugText.push(`${'-'.repeat(60)}\\n${page.body}`);\n\t\t}\n\t\tdebug(getBlock('RESPONSE', debugText.join('\\n')));\n\t}\n\t/* v8 ignore stop */\n\n\tif (page.body instanceof stream.Readable) {\n\t\tawait pipeReadableToResponse(page.body, res);\n\t} else {\n\t\tres.send(page.body);\n\t}\n};","/*\n *\tRequestError\n */\n\nimport type {environmentType} from '../../types.ts';\nimport type {BindParameters} from 'oracledb';\n\nexport class ProcedureError extends Error {\n\ttimestamp: Date;\n\tenvironment: environmentType;\n\tsql: string;\n\tbind: BindParameters;\n\n\t/**\n\t * @param message - The error message.\n\t * @param environment - The environment.\n\t * @param sql - The SQL to execute.\n\t * @param bind - The bind parameters.\n\t */\n\tconstructor(message: string, environment: environmentType, sql: string, bind: BindParameters) {\n\t\tsuper(message);\n\n\t\t// Maintains proper stack trace for where our error was thrown (only available on V8)\n\t\tif (Error.captureStackTrace) {\n\t\t\tError.captureStackTrace(this, ProcedureError);\n\t\t}\n\n\t\t// Custom debugging information\n\t\tthis.timestamp = new Date();\n\t\tthis.environment = environment;\n\t\tthis.sql = sql;\n\t\tthis.bind = bind;\n\t}\n}","import {DEFAULT_CACHE_MAX_SIZE, CACHE_PRUNE_PERCENT} from '../../common/constants.ts';\n\ntype cacheEntryType<T> = {\n\thitCount: number;\n\tvalue: T;\n};\n\n/**\n * Generic Cache class with LFU (Least Frequently Used) eviction policy.\n */\nexport class Cache<T> {\n\tcache: Map<string, cacheEntryType<T>>;\n\tmaxSize: number;\n\thits: number;\n\tmisses: number;\n\n\t/**\n\t * @param maxSize - Maximum number of entries in the cache.\n\t */\n\tconstructor(maxSize: number = DEFAULT_CACHE_MAX_SIZE) {\n\t\tthis.cache = new Map();\n\t\tthis.maxSize = maxSize;\n\t\tthis.hits = 0;\n\t\tthis.misses = 0;\n\t}\n\n\t/**\n\t * Get an entry from the cache.\n\t * @param key - The key.\n\t * @returns The value or undefined if not found.\n\t */\n\tget(key: string): T | undefined {\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry) {\n\t\t\tentry.hitCount++;\n\t\t\tthis.hits++;\n\t\t\treturn entry.value;\n\t\t}\n\t\tthis.misses++;\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Set an entry in the cache.\n\t * @param key - The key.\n\t * @param value - The value.\n\t */\n\tset(key: string, value: T): void {\n\t\t// If updating an existing key, preserve its hitCount?\n\t\t// Typically LFU implies resetting or keeping.\n\t\t// For simplicity and avoiding complex aging, if we set it again, we reset hitCount or keep it?\n\t\t// The requirement is \"cache invalidation\" (delete) or \"cache loading\" (set).\n\t\t// If we overwrite, it's usually a new value. Let's reset hitCount to 0 for a fresh start or 1.\n\n\t\t// Ensure we have space\n\t\tif (this.cache.size >= this.maxSize && !this.cache.has(key)) {\n\t\t\tthis.prune();\n\t\t}\n\n\t\tthis.cache.set(key, {hitCount: 0, value});\n\t}\n\n\t/**\n\t * Delete an entry from the cache.\n\t * @param key - The key.\n\t */\n\tdelete(key: string): void {\n\t\tthis.cache.delete(key);\n\t}\n\n\t/**\n\t * Clear the cache.\n\t */\n\tclear(): void {\n\t\tthis.cache.clear();\n\t\tthis.hits = 0;\n\t\tthis.misses = 0;\n\t}\n\n\t/**\n\t * Prune the cache by removing the least frequently used entries.\n\t * Removes 10% of the cache size.\n\t */\n\tprune(): void {\n\t\t// Convert cache entries to an array\n\t\tconst entries = Array.from(this.cache.entries());\n\n\t\t// Sort entries by hitCount in ascending order\n\t\tentries.sort((a, b) => a[1].hitCount - b[1].hitCount);\n\n\t\t// Remove the bottom 10%\n\t\tconst removeCount = Math.max(1, Math.floor(this.maxSize * CACHE_PRUNE_PERCENT));\n\t\tconst keysToRemove = entries.slice(0, removeCount).map(([key]) => key);\n\n\t\tfor (const key of keysToRemove) {\n\t\t\tthis.cache.delete(key);\n\t\t}\n\t}\n\n\t/**\n\t * Get the size of the cache.\n\t * @returns The size.\n\t */\n\tget size(): number {\n\t\treturn this.cache.size;\n\t}\n\n\t/**\n\t * Get all keys in the cache.\n\t * @returns The keys.\n\t */\n\tkeys(): string[] {\n\t\treturn Array.from(this.cache.keys());\n\t}\n\n\t/**\n\t * Get cache statistics.\n\t * @returns The statistics.\n\t */\n\tgetStats(): {size: number; maxSize: number; hits: number; misses: number} {\n\t\treturn {\n\t\t\tsize: this.cache.size,\n\t\t\tmaxSize: this.maxSize,\n\t\t\thits: this.hits,\n\t\t\tmisses: this.misses,\n\t\t};\n\t}\n}","import debugModule from 'debug';\nconst debug = debugModule('webplsql:procedureSanitize');\n\nimport {BIND_IN, BIND_OUT, STRING, NUMBER} from '../../util/oracledb-provider.ts';\nimport z from 'zod';\nimport {RequestError} from './requestError.ts';\nimport {errorToString} from '../../util/errorToString.ts';\nimport {OWA_RESOLVED_NAME_MAX_LEN} from '../../../common/constants.ts';\nimport type {Connection, Result, BindParameters} from 'oracledb';\nimport type {configPlSqlHandlerType, ProcedureNameCache} from '../../types.ts';\nimport {Cache} from '../../util/cache.ts';\n\nconst DEFAULT_EXCLUSION_LIST = ['sys.', 'dbms_', 'utl_', 'owa_', 'htp.', 'htf.', 'wpg_docload.', 'ctxsys.', 'mdsys.'];\n\nconst validationFunctionCache = new Cache<{valid: boolean}>();\n\n/**\n * Resolve the procedure name using dbms_utility.name_resolve.\n *\n * @param procName - The procedure name to resolve.\n * @param databaseConnection - The database connection.\n * @param procedureNameCache - The procedure name cache.\n * @returns The resolved canonical procedure name (SCHEMA.NAME).\n */\nconst resolveProcedureName = async (procName: string, databaseConnection: Connection, procedureNameCache: ProcedureNameCache): Promise<string> => {\n\t// Check cache\n\tconst cachedName = procedureNameCache.get(procName);\n\tif (cachedName) {\n\t\tdebug(`resolveProcedureName: Cache hit for \"${procName}\" -> \"${cachedName}\"`);\n\t\treturn cachedName;\n\t}\n\n\tdebug(`resolveProcedureName: Cache miss for \"${procName}\". Resolving in DB...`);\n\n\tconst sql = `\n\t\tDECLARE\n\t\t\tl_schema VARCHAR2(128);\n\t\t\tl_part1  VARCHAR2(128);\n\t\t\tl_part2  VARCHAR2(128);\n\t\t\tl_dblink VARCHAR2(128);\n\t\t\tl_part1_type NUMBER;\n\t\t\tl_object_number NUMBER;\n\t\tBEGIN\n\t\t\tdbms_utility.name_resolve(\n\t\t\t\tname => :name,\n\t\t\t\tcontext => 1,\n\t\t\t\tschema => l_schema,\n\t\t\t\tpart1 => l_part1,\n\t\t\t\tpart2 => l_part2,\n\t\t\t\tdblink => l_dblink,\n\t\t\t\tpart1_type => l_part1_type,\n\t\t\t\tobject_number => l_object_number\n\t\t\t);\n\t\t\t\n\t\t\t-- Reconstruct the canonical name\n\t\t\t-- If it's a package procedure: schema.package.procedure\n\t\t\t-- If it's a standalone procedure: schema.procedure\n\t\t\t\n\t\t\tIF l_part1 IS NOT NULL THEN\n\t\t\t\t:resolved := l_schema || '.' || l_part1 || '.' || l_part2;\n\t\t\tELSE\n\t\t\t\t:resolved := l_schema || '.' || l_part2;\n\t\t\tEND IF;\n\t\tEND;\n\t`;\n\n\tconst bind: BindParameters = {\n\t\tname: {dir: BIND_IN, type: STRING, val: procName},\n\t\tresolved: {dir: BIND_OUT, type: STRING, maxSize: OWA_RESOLVED_NAME_MAX_LEN},\n\t};\n\n\ttry {\n\t\tconst result = await databaseConnection.execute(sql, bind);\n\t\tconst {resolved} = z.strictObject({resolved: z.string()}).parse(result.outBinds);\n\n\t\tif (!resolved) {\n\t\t\tthrow new RequestError(`Could not resolve procedure name \"${procName}\"`);\n\t\t}\n\n\t\tdebug(`resolveProcedureName: Resolved \"${procName}\" -> \"${resolved}\"`);\n\n\t\t// Update cache\n\t\tprocedureNameCache.set(procName, resolved);\n\n\t\treturn resolved;\n\t} catch (err) {\n\t\t/* v8 ignore start */\n\t\tdebug(`resolveProcedureName: Error resolving \"${procName}\"`, err);\n\t\t/* v8 ignore stop */\n\n\t\t// Rethrow as RequestError to indicate 404/403\n\t\tthrow new RequestError(`Procedure \"${procName}\" not found or not accessible.\\n${errorToString(err)}`);\n\t}\n};\n\n/**\n * Sanitize the procedure name.\n *\n * @param procName - The procedure name.\n * @param databaseConnection - The database connection\n * @param options - the options for the middleware.\n * @param procedureNameCache - The procedure name cache.\n * @returns Promise resolving to final procedure name.\n */\nexport const sanitizeProcName = async (\n\tprocName: string,\n\tdatabaseConnection: Connection,\n\toptions: configPlSqlHandlerType,\n\tprocedureNameCache: ProcedureNameCache,\n): Promise<string> => {\n\tdebug('sanitizeProcName', procName);\n\n\t// make lowercase and trim\n\tlet finalProcName = procName.toLowerCase().trim();\n\n\t// remove special characters (basic sanity check before DB call)\n\tfinalProcName = removeSpecialCharacters(finalProcName);\n\n\t// check for default exclusions\n\tfor (const i of DEFAULT_EXCLUSION_LIST) {\n\t\tif (finalProcName.startsWith(i)) {\n\t\t\tconst error = `Procedure name \"${procName}\" is in default exclusion list \"${DEFAULT_EXCLUSION_LIST.join(',')}\"`;\n\t\t\t/* v8 ignore start */\n\t\t\tdebug(error);\n\t\t\t/* v8 ignore stop */\n\n\t\t\tthrow new RequestError(error);\n\t\t}\n\t}\n\n\t// check for custom exclusions\n\tif (options.exclusionList && options.exclusionList.length > 0) {\n\t\tfor (const i of options.exclusionList) {\n\t\t\tif (finalProcName.startsWith(i)) {\n\t\t\t\tconst error = `Procedure name \"${procName}\" is in custom exclusion list \"${options.exclusionList.join(',')}\"`;\n\t\t\t\t/* v8 ignore start */\n\t\t\t\tdebug(error);\n\t\t\t\t/* v8 ignore stop */\n\n\t\t\t\tthrow new RequestError(error);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Check request validation function\n\tif (options.requestValidationFunction && options.requestValidationFunction.length > 0) {\n\t\t// Note: We might want to scope this cache too, but for now let's focus on the procedure name cache.\n\t\t// The original code used a global cache for this.\n\t\t// For strict correctness, we should probably verify the *resolved* name,\n\t\t// but legacy behavior checks the input name.\n\t\tconst valid = await requestValidationFunction(finalProcName, options.requestValidationFunction, databaseConnection);\n\t\tif (!valid) {\n\t\t\tconst error = `Procedure name \"${procName}\" is not valid according to the request validation function \"${options.requestValidationFunction}\"`;\n\t\t\t/* v8 ignore start */\n\t\t\tdebug(error);\n\t\t\t/* v8 ignore stop */\n\n\t\t\tthrow new RequestError(error);\n\t\t}\n\t}\n\n\t// NEW: Resolve the procedure name against the database\n\t// This prevents SQL injection and ensures the procedure exists\n\tconst resolvedName = await resolveProcedureName(finalProcName, databaseConnection, procedureNameCache);\n\n\treturn resolvedName;\n};\n\n/**\n * @param str - string\n * @returns string\n */\nconst removeSpecialCharacters = (str: string | null | undefined): string => {\n\tif (str === null || str === undefined) {\n\t\treturn '';\n\t}\n\n\tconst chars: string[] = [];\n\n\tfor (const c of str) {\n\t\tif ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c === '.' || c === '_' || c === '#' || c === '$') {\n\t\t\tchars.push(c);\n\t\t}\n\t}\n\n\treturn chars.join('');\n};\n\n/**\n * @param procName - The procedure name.\n * @param requestValidationFunction -  The request validation function.\n * @param databaseConnection - The database connection\n * @returns Promise resolving to final procedure name.\n */\nconst loadRequestValid = async (procName: string, requestValidationFunction: string, databaseConnection: Connection): Promise<boolean> => {\n\tconst bind: BindParameters = {\n\t\tproc: {dir: BIND_IN, type: STRING, val: procName},\n\t\tvalid: {dir: BIND_OUT, type: NUMBER},\n\t};\n\n\tconst SQL = [\n\t\t'DECLARE',\n\t\t'   l_valid NUMBER := 0;',\n\t\t'BEGIN',\n\t\t`   IF (${requestValidationFunction}(:proc)) THEN`,\n\t\t'      l_valid := 1;',\n\t\t'   END IF;',\n\t\t'   :valid := l_valid;',\n\t\t'END;',\n\t].join('\\n');\n\n\tlet result: Result<unknown> = {};\n\ttry {\n\t\tresult = await databaseConnection.execute(SQL, bind);\n\t} catch (err) {\n\t\t/* v8 ignore start */\n\t\tdebug('result', result);\n\t\t/* v8 ignore stop */\n\n\t\tconst message = `Error when validating procedure name \"${procName}\"\\n${SQL}\\n${errorToString(err)}`;\n\t\tthrow new RequestError(message);\n\t}\n\n\ttry {\n\t\tconst data = z.strictObject({valid: z.number()}).parse(result.outBinds);\n\t\treturn data.valid === 1;\n\t} catch (err) {\n\t\t/* v8 ignore start */\n\t\tdebug('result', result.outBinds);\n\t\t/* v8 ignore stop */\n\n\t\tconst message = `Internal error when parsing ${String(result.outBinds)}\\n${errorToString(err)}`;\n\t\tthrow new Error(message);\n\t}\n};\n\n/**\n * Request validation function.\n *\n * @param procName - The procedure name.\n * @param validationFunction -  The request validation function.\n * @param databaseConnection - The database connection\n * @returns Promise resolving to final procedure name.\n */\nconst requestValidationFunction = async (procName: string, validationFunction: string, databaseConnection: Connection): Promise<boolean> => {\n\tdebug('requestValidationFunction', procName, validationFunction);\n\n\t// calculate the key\n\tconst key = procName.toLowerCase();\n\n\t// lookup in the cache\n\tconst cacheEntry = validationFunctionCache.get(key);\n\n\t// if we found the procedure in the cache, we return it (hitCount already incremented by get)\n\tif (cacheEntry !== undefined) {\n\t\t/* v8 ignore start */\n\t\tif (debug.enabled) {\n\t\t\tdebug(`requestValidationFunction: procedure \"${procName}\" found in cache`);\n\t\t}\n\t\t/* v8 ignore stop */\n\n\t\treturn cacheEntry.valid;\n\t}\n\n\t// load from database\n\t/* v8 ignore start */\n\tif (debug.enabled) {\n\t\tdebug(`requestValidationFunction: procedure \"${procName}\" not found in cache and must be loaded`);\n\t}\n\t/* v8 ignore stop */\n\n\tconst valid = await loadRequestValid(procName, validationFunction, databaseConnection);\n\n\t// add to the cache\n\tvalidationFunctionCache.set(key, {valid});\n\n\treturn valid;\n};","import {Readable} from 'node:stream';\nimport {BIND_OUT, BIND_INOUT, STRING, NUMBER} from '../../util/oracledb-provider.ts';\nimport z from 'zod';\nimport debugModule from 'debug';\nimport {ProcedureError} from './procedureError.ts';\nimport {errorToString} from '../../util/errorToString.ts';\nimport {OWA_STREAM_CHUNK_SIZE, OWA_STREAM_BUFFER_SIZE} from '../../../common/constants.ts';\nimport type {Connection, BindParameters} from 'oracledb';\n\nconst debug = debugModule('webplsql:owaPageStream');\n\nexport class OWAPageStream extends Readable {\n\tdatabaseConnection: Connection;\n\tchunkSize: number;\n\tisDone: boolean;\n\n\t/**\n\t * @param databaseConnection - The database connection.\n\t */\n\tconstructor(databaseConnection: Connection) {\n\t\tsuper({highWaterMark: OWA_STREAM_BUFFER_SIZE}); // 16KB buffer\n\t\tthis.databaseConnection = databaseConnection;\n\t\tthis.chunkSize = OWA_STREAM_CHUNK_SIZE;\n\t\tthis.isDone = false;\n\t}\n\n\t/**\n\t * Fetch a chunk of the page from the database.\n\t * @returns The array of lines fetched.\n\t */\n\tasync fetchChunk(): Promise<string[]> {\n\t\tif (this.isDone) return [];\n\n\t\tconst bindParameter: BindParameters = {\n\t\t\tlines: {dir: BIND_OUT, type: STRING, maxArraySize: this.chunkSize},\n\t\t\tirows: {dir: BIND_INOUT, type: NUMBER, val: this.chunkSize},\n\t\t};\n\n\t\tconst sqlStatement = 'BEGIN owa.get_page(thepage=>:lines, irows=>:irows); END;';\n\n\t\ttry {\n\t\t\tconst result = await this.databaseConnection.execute(sqlStatement, bindParameter);\n\t\t\tconst {lines, irows} = z.strictObject({irows: z.number(), lines: z.array(z.string())}).parse(result.outBinds);\n\n\t\t\tdebug(`fetched ${lines.length} lines (irows=${irows})`);\n\n\t\t\t// If we got fewer lines than requested, OR if we got 0 lines, we are done\n\t\t\tif (lines.length < this.chunkSize) {\n\t\t\t\tthis.isDone = true;\n\t\t\t}\n\n\t\t\treturn lines;\n\t\t} catch (err) {\n\t\t\tif (err instanceof ProcedureError) {\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t\tthrow new ProcedureError(`OWAPageStream: error when getting page\\n${errorToString(err)}`, {}, sqlStatement, bindParameter);\n\t\t}\n\t}\n\n\t/**\n\t * @override\n\t * @param _size - The size hint (unused).\n\t */\n\toverride _read(_size: number): void {\n\t\tthis.fetchChunk()\n\t\t\t.then((lines) => {\n\t\t\t\tif (lines.length > 0) {\n\t\t\t\t\tthis.push(lines.join(''));\n\t\t\t\t}\n\n\t\t\t\t// After fetching, check if we're done\n\t\t\t\tif (this.isDone) {\n\t\t\t\t\tthis.push(null);\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch((err: unknown) => {\n\t\t\t\tthis.destroy(err instanceof Error ? err : new Error(String(err)));\n\t\t\t});\n\t}\n\n\t/**\n\t * Add initial body content to the stream.\n\t * @param content - The initial content to prepend.\n\t */\n\taddBody(content: string): void {\n\t\tif (content && content.length > 0) {\n\t\t\tthis.push(content);\n\t\t}\n\t}\n}","import fs from 'node:fs';\nimport path from 'node:path';\nimport type {procedureTraceEntry} from '../types.ts';\n\nclass TraceManager {\n\tenabled = false;\n\tfilename = 'trace.json.log';\n\tmaxEntries = 1000;\n\n\t/**\n\t * Toggle tracing\n\t * @param enabled - New state\n\t */\n\tsetEnabled(enabled: boolean): void {\n\t\tthis.enabled = enabled;\n\t}\n\n\t/**\n\t * Is tracing enabled?\n\t * @returns The state\n\t */\n\tisEnabled(): boolean {\n\t\treturn this.enabled;\n\t}\n\n\t/**\n\t * Add a trace entry\n\t * @param entry - The trace entry\n\t */\n\taddTrace(entry: procedureTraceEntry): void {\n\t\tif (!this.enabled) return;\n\n\t\ttry {\n\t\t\tconst line = JSON.stringify(entry) + '\\n';\n\t\t\tfs.appendFileSync(this.filename, line);\n\t\t} catch (err) {\n\t\t\tconsole.error('TraceManager: error writing trace', err);\n\t\t}\n\t}\n\n\t/**\n\t * Clear all traces\n\t */\n\tclear(): void {\n\t\ttry {\n\t\t\tif (fs.existsSync(this.filename)) {\n\t\t\t\tfs.truncateSync(this.filename, 0);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.error('TraceManager: error clearing traces', err);\n\t\t}\n\t}\n\n\t/**\n\t * Get the full path to the trace file\n\t * @returns The path\n\t */\n\tgetFilePath(): string {\n\t\treturn path.resolve(this.filename);\n\t}\n}\n\nexport const traceManager = new TraceManager();","/*\n *\tInvoke the Oracle procedure and return the raw content of the page\n */\n\nimport debugModule from 'debug';\nconst debug = debugModule('webplsql:procedure');\n\nimport {BIND_IN, BIND_OUT, BIND_INOUT, STRING, NUMBER, BLOB} from '../../util/oracledb-provider.ts';\nimport stream from 'node:stream';\nimport z from 'zod';\n\nimport {uploadFile} from './upload.ts';\nimport {getProcedureVariable} from './procedureVariable.ts';\nimport {getProcedureNamed} from './procedureNamed.ts';\nimport {parsePage} from './parsePage.ts';\nimport {sendResponse} from './sendResponse.ts';\nimport {ProcedureError} from './procedureError.ts';\nimport {RequestError} from './requestError.ts';\nimport {inspect, getBlock} from '../../util/trace.ts';\nimport {errorToString} from '../../util/errorToString.ts';\nimport {sanitizeProcName} from './procedureSanitize.ts';\nimport {OWAPageStream} from './owaPageStream.ts';\nimport {traceManager} from '../../util/traceManager.ts';\nimport type {procedureTraceEntry} from '../../../frontend/types.ts';\nimport type {Request, Response} from 'express';\nimport type {Connection, Result, Lob, BindParameters} from 'oracledb';\nimport type {argObjType, fileUploadType, environmentType, configPlSqlHandlerType, ProcedureNameCache, ArgumentCache} from '../../types.ts';\n\n/**\n *\tGet the procedure and arguments to execute\n *\t@param req - The req object represents the HTTP request. (only used for debugging)\n *\t@param procName - The procedure to execute\n *\t@param argObj - The arguments to pass to the procedure\n *\t@param options - The options for the middleware\n *\t@param databaseConnection - The database connection\n *\t@param procedureNameCache - The procedure name cache.\n *\t@param argumentCache - The argument cache.\n *\t@returns The SQL statement and bindings for the procedure to execute\n */\nconst getProcedure = async (\n\treq: Request,\n\tprocName: string,\n\targObj: argObjType,\n\toptions: configPlSqlHandlerType,\n\tdatabaseConnection: Connection,\n\tprocedureNameCache: ProcedureNameCache,\n\targumentCache: ArgumentCache,\n): Promise<{sql: string; bind: BindParameters; resolvedName?: string}> => {\n\t// path alias\n\tif (options.pathAlias?.toLowerCase() === procName.toLowerCase()) {\n\t\t/* v8 ignore start */\n\t\tdebug(`getProcedure: path alias \"${options.pathAlias}\" redirects to \"${options.pathAliasProcedure}\"`);\n\t\t/* v8 ignore stop */\n\t\treturn {\n\t\t\tsql: `${options.pathAliasProcedure}(p_path=>:p_path)`,\n\t\t\tbind: {\n\t\t\t\tp_path: {dir: BIND_IN, type: STRING, val: procName},\n\t\t\t},\n\t\t};\n\t}\n\n\t// check if we use variable arguments\n\tconst useVariableArguments = procName.startsWith('!');\n\n\t// sanitize procedure name\n\tconst rawName = useVariableArguments ? procName.slice(1) : procName;\n\tconst sanitizedProcName = await sanitizeProcName(rawName, databaseConnection, options, procedureNameCache);\n\n\t// run procedure\n\treturn useVariableArguments\n\t\t? {\n\t\t\t\t...getProcedureVariable(req, sanitizedProcName, argObj),\n\t\t\t\tresolvedName: sanitizedProcName,\n\t\t\t}\n\t\t: {\n\t\t\t\t...(await getProcedureNamed(req, sanitizedProcName, argObj, databaseConnection, argumentCache)),\n\t\t\t\tresolvedName: sanitizedProcName,\n\t\t\t};\n};\n\n/**\n * Prepare procedure\n *\n * NOTE:\n * \t1) dbms_session.modify_package_state(dbms_session.reinitialize) is used to ensure a stateless environment by resetting package state (dbms_session.reset_package)\n *\n * @param cgiObj - The cgi of the procedure to invoke.\n * @param databaseConnection - Database connection.\n * @returns Promise resolving to void.\n */\nconst procedurePrepare = async (cgiObj: environmentType, databaseConnection: Connection): Promise<void> => {\n\tlet sqlStatement = 'BEGIN dbms_session.modify_package_state(dbms_session.reinitialize); END;';\n\ttry {\n\t\tawait databaseConnection.execute(sqlStatement);\n\t} catch (err) {\n\t\tthrow new ProcedureError(`procedurePrepare: error when preparing procedure\\n${errorToString(err)}`, cgiObj, sqlStatement, {});\n\t}\n\n\t// htbuf_len: reduce this limit based on your worst-case character size.\n\t// For most character sets, this will be 2 bytes per character, so the limit would be 127.\n\t// For UTF8 Unicode, it's 3 bytes per character, meaning the limit should be 85.\n\t// For the newer AL32UTF8 Unicode, it's 4 bytes per character, and the limit should be 63.\n\tsqlStatement = 'BEGIN owa.init_cgi_env(:cgicount, :cginames, :cgivalues); owa.user_id := :remote_user; htp.init; htp.htbuf_len := 63; END;';\n\tconst bindParameter: BindParameters = {\n\t\tcgicount: {dir: BIND_IN, type: NUMBER, val: Object.keys(cgiObj).length},\n\t\tcginames: {dir: BIND_IN, type: STRING, val: Object.keys(cgiObj)},\n\t\tcgivalues: {dir: BIND_IN, type: STRING, val: Object.values(cgiObj)},\n\t\tremote_user: {dir: BIND_IN, type: STRING, val: (cgiObj.REMOTE_USER ?? '').slice(0, 30)},\n\t};\n\ttry {\n\t\tawait databaseConnection.execute(sqlStatement, bindParameter);\n\t} catch (err) {\n\t\tthrow new ProcedureError(`procedurePrepare: error when preparing procedure\\n${errorToString(err)}`, cgiObj, sqlStatement, bindParameter);\n\t}\n};\n\n/**\n * Execute procedure\n *\n * @param para - The statement and binding to use when executing the procedure.\n * @param para.sql - The SQL statement.\n * @param para.bind - The bind parameters.\n * @param databaseConnection - Database connection.\n * @returns Promise resolving to void.\n */\nconst procedureExecute = async (para: {sql: string; bind: BindParameters}, databaseConnection: Connection): Promise<void> => {\n\tconst sqlStatement = `BEGIN ${para.sql}; END;`;\n\n\ttry {\n\t\tawait databaseConnection.execute(sqlStatement, para.bind);\n\t} catch (err) {\n\t\tthrow new ProcedureError(`procedureExecute: error when executing procedure:\\n${sqlStatement}\\n${errorToString(err)}`, {}, para.sql, para.bind);\n\t}\n};\n\n/**\n * Download files from procedure\n *\n * @param fileBlob - The blob eventually containing the file.\n * @param databaseConnection - Database connection.\n * @returns Promise resolving to the result.\n */\nconst procedureDownloadFiles = async (\n\tfileBlob: Lob,\n\tdatabaseConnection: Connection,\n): Promise<{fileType: string; fileSize: number; fileBlob: stream.Readable | null}> => {\n\tconst bindParameter: BindParameters = {\n\t\tfileType: {dir: BIND_OUT, type: STRING},\n\t\tfileSize: {dir: BIND_OUT, type: NUMBER},\n\t\tfileBlob: {dir: BIND_INOUT, type: BLOB, val: fileBlob},\n\t};\n\n\tconst sqlStatement = `\nDECLARE\n\tl_file_type\t\tVARCHAR2(32767)\t:= '';\n\tl_file_size\t\tINTEGER\t\t\t:= 0;\nBEGIN\n\tIF (wpg_docload.is_file_download()) THEN\n\t\twpg_docload.get_download_file(l_file_type);\n\t\tIF (l_file_type = 'B') THEN\n\t\t\twpg_docload.get_download_blob(:fileBlob);\n\t\t\tl_file_size := dbms_lob.getlength(:fileBlob);\n\t\tEND IF;\n\tEND IF;\n\t:fileType := l_file_type;\n\t:fileSize := l_file_size;\nEND;\n`;\n\n\tlet result: Result<unknown> | null = null;\n\ttry {\n\t\tresult = await databaseConnection.execute(sqlStatement, bindParameter);\n\t} catch (err) {\n\t\t/* v8 ignore start */\n\t\tif (debug.enabled) {\n\t\t\tdebug(getBlock('procedureDownloadFiles: results', inspect(result)));\n\t\t}\n\t\t/* v8 ignore stop */\n\n\t\tthrow new ProcedureError(`procedureDownloadFiles: error when downloading files\\n${errorToString(err)}`, {}, sqlStatement, bindParameter);\n\t}\n\n\treturn z\n\t\t.object({\n\t\t\tfileType: z\n\t\t\t\t.string()\n\t\t\t\t.nullable()\n\t\t\t\t.transform((val) => val ?? ''),\n\t\t\tfileSize: z\n\t\t\t\t.number()\n\t\t\t\t.nullable()\n\t\t\t\t.transform((val) => val ?? 0),\n\t\t\tfileBlob: z.instanceof(stream.Readable).nullable(),\n\t\t})\n\t\t.parse(result.outBinds);\n};\n\n/**\n * Invoke the Oracle procedure and return the page content\n *\n * @param req - The req object represents the HTTP request.\n * @param res - The res object represents the HTTP response that an Express app sends when it gets an HTTP request.\n * @param argObj - - The arguments of the procedure to invoke.\n * @param cgiObj - The cgi of the procedure to invoke.\n * @param filesToUpload - Array of files to be uploaded\n * @param options - the options for the middleware.\n * @param databaseConnection - Database connection.\n * @param procedureNameCache - The procedure name cache.\n * @param argumentCache - The argument cache.\n * @returns Promise resolving to the page content generated by the executed procedure\n */\nexport const invokeProcedure = async (\n\treq: Request,\n\tres: Response,\n\targObj: argObjType,\n\tcgiObj: environmentType,\n\tfilesToUpload: fileUploadType[],\n\toptions: configPlSqlHandlerType,\n\tdatabaseConnection: Connection,\n\tprocedureNameCache: ProcedureNameCache,\n\targumentCache: ArgumentCache,\n): Promise<void> => {\n\tdebug('invokeProcedure: begin');\n\n\tconst startTime = Date.now();\n\tlet traceData: procedureTraceEntry | null = null;\n\n\tif (traceManager.isEnabled()) {\n\t\ttraceData = {\n\t\t\tid: Math.random().toString(36).slice(2, 15),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tsource: cgiObj.REMOTE_ADDR ?? '',\n\t\t\turl: req.originalUrl,\n\t\t\tmethod: req.method,\n\t\t\tstatus: 'pending',\n\t\t\tduration: 0,\n\t\t\tcgi: cgiObj,\n\t\t\theaders: req.headers as Record<string, string>,\n\t\t\tcookies: req.cookies,\n\t\t\tuploads: filesToUpload.map((f) => ({\n\t\t\t\toriginalname: f.originalname,\n\t\t\t\tmimetype: f.mimetype,\n\t\t\t\tsize: f.size,\n\t\t\t})),\n\t\t};\n\t}\n\n\ttry {\n\t\t// 1) upload files\n\t\tdebug(`invokeProcedure: upload \"${filesToUpload.length}\" files`);\n\t\tif (filesToUpload.length > 0) {\n\t\t\tif (typeof options.documentTable === 'string' && options.documentTable.length > 0) {\n\t\t\t\tconst {documentTable} = options;\n\n\t\t\t\tawait Promise.all(filesToUpload.map((file) => uploadFile(file, documentTable, databaseConnection)));\n\t\t\t} else {\n\t\t\t\t// FIXME: this should be standartized\n\t\t\t\tconsole.warn(`Unable to upload \"${filesToUpload.length}\" files because the option \"\"doctable\" has not been defined`);\n\t\t\t}\n\t\t}\n\n\t\t// 2) get procedure to execute and the arguments\n\n\t\tdebug('invokeProcedure: get procedure to execute and the arguments');\n\t\t// Extract the raw procedure name from params\n\t\tconst rawProcName = Array.isArray(req.params.name) ? req.params.name[0] : req.params.name;\n\t\tif (!rawProcName) {\n\t\t\tthrow new RequestError('No procedure name provided');\n\t\t}\n\t\tconst para = await getProcedure(req, rawProcName, argObj, options, databaseConnection, procedureNameCache, argumentCache);\n\n\t\tif (traceData) {\n\t\t\ttraceData.procedure = para.resolvedName;\n\t\t\ttraceData.parameters = para.bind as Record<string, unknown> | unknown[];\n\t\t}\n\n\t\t// 3) prepare the session\n\n\t\tdebug('invokeProcedure: prepare the session');\n\t\tawait procedurePrepare(cgiObj, databaseConnection);\n\n\t\t// 4) execute the procedure\n\n\t\tdebug('invokeProcedure: execute the session');\n\t\ttry {\n\t\t\tawait procedureExecute(para, databaseConnection);\n\t\t} catch (err) {\n\t\t\t// Invalidation Logic\n\t\t\tif (err instanceof ProcedureError) {\n\t\t\t\tconst errorString = err.toString();\n\t\t\t\t// Check for ORA-04068, ORA-04061, ORA-04065, ORA-06550\n\t\t\t\tif (errorString.includes('ORA-04068') || errorString.includes('ORA-04061') || errorString.includes('ORA-04065') || errorString.includes('ORA-06550')) {\n\t\t\t\t\tdebug(`invokeProcedure: detected invalidation error (${errorString}). Clearing caches.`);\n\n\t\t\t\t\t// Clear name resolution cache for the input name\n\t\t\t\t\tif (rawProcName) {\n\t\t\t\t\t\tprocedureNameCache.delete(rawProcName);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Clear argument cache for the resolved name\n\t\t\t\t\tif (para.resolvedName) {\n\t\t\t\t\t\targumentCache.delete(para.resolvedName.toUpperCase());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\n\t\t// 5) get the page returned from the procedure\n\t\tdebug('invokeProcedure: get the page returned from the procedure');\n\n\t\tconst streamInstance = new OWAPageStream(databaseConnection);\n\t\tconst lines = await streamInstance.fetchChunk();\n\n\t\t/* v8 ignore start */\n\t\tif (debug.enabled) {\n\t\t\tdebug(getBlock('data', lines.join('')));\n\t\t}\n\t\t/* v8 ignore stop */\n\n\t\t// 6) download files\n\n\t\tdebug('invokeProcedure: download files');\n\t\tconst fileBlob = await databaseConnection.createLob(BLOB);\n\n\t\ttry {\n\t\t\tconst fileDownload = await procedureDownloadFiles(fileBlob, databaseConnection);\n\t\t\t/* v8 ignore start */\n\t\t\tif (debug.enabled) {\n\t\t\t\tdebug(getBlock('fileDownload', inspect({fileType: fileDownload.fileType, fileSize: fileDownload.fileSize})));\n\t\t\t}\n\t\t\t/* v8 ignore stop */\n\n\t\t\t// 7) parse the page\n\n\t\t\tdebug('invokeProcedure: parse the page');\n\t\t\t// We parse the headers from the first chunk\n\t\t\tconst pageComponents = parsePage(lines.join(''));\n\n\t\t\t// add \"Server\" header\n\t\t\tpageComponents.head.server = cgiObj.SERVER_SOFTWARE ?? '';\n\n\t\t\t// add file download information\n\t\t\tif (fileDownload.fileType !== '' && fileDownload.fileSize > 0 && fileDownload.fileBlob !== null) {\n\t\t\t\tpageComponents.file.fileType = fileDownload.fileType;\n\t\t\t\tpageComponents.file.fileSize = fileDownload.fileSize;\n\t\t\t\tpageComponents.file.fileBlob = fileDownload.fileBlob;\n\n\t\t\t\tif (traceData) {\n\t\t\t\t\ttraceData.downloads = {\n\t\t\t\t\t\tfileType: fileDownload.fileType,\n\t\t\t\t\t\tfileSize: fileDownload.fileSize,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// For normal pages, we use the stream.\n\t\t\t\t// Prepend the initial body (parsed by parsePage) to the stream\n\t\t\t\tif (typeof pageComponents.body === 'string' && pageComponents.body.length > 0) {\n\t\t\t\t\tstreamInstance.addBody(pageComponents.body);\n\t\t\t\t}\n\t\t\t\t// Use the stream as the body\n\t\t\t\tpageComponents.body = streamInstance;\n\n\t\t\t\tif (traceData) {\n\t\t\t\t\t// Buffer HTML if tracing enabled\n\t\t\t\t\tlet htmlBuffer = typeof pageComponents.body === 'string' ? pageComponents.body : lines.join('');\n\t\t\t\t\tconst MAX_HTML_SIZE = 1024 * 1024; // 1MB\n\n\t\t\t\t\tconst originalPush = streamInstance.push.bind(streamInstance);\n\t\t\t\t\tstreamInstance.push = (chunk) => {\n\t\t\t\t\t\tif (chunk !== null && htmlBuffer.length < MAX_HTML_SIZE) {\n\t\t\t\t\t\t\tconst str = String(chunk);\n\t\t\t\t\t\t\thtmlBuffer += str;\n\t\t\t\t\t\t\tif (htmlBuffer.length > MAX_HTML_SIZE) {\n\t\t\t\t\t\t\t\thtmlBuffer = htmlBuffer.slice(0, Math.max(0, MAX_HTML_SIZE)) + '... [truncated]';\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn originalPush(chunk);\n\t\t\t\t\t};\n\n\t\t\t\t\t// Since we can't easily wait for the stream to finish here without blocking,\n\t\t\t\t\t// we'll have to finalize traceData when the stream ends.\n\t\t\t\t\tconst currentTrace = traceData;\n\t\t\t\t\tstreamInstance.on('end', () => {\n\t\t\t\t\t\tcurrentTrace.html = htmlBuffer;\n\t\t\t\t\t\tcurrentTrace.status = 'success';\n\t\t\t\t\t\tcurrentTrace.duration = Date.now() - startTime;\n\t\t\t\t\t\ttraceManager.addTrace(currentTrace);\n\t\t\t\t\t});\n\t\t\t\t\tstreamInstance.on('error', (err) => {\n\t\t\t\t\t\tcurrentTrace.status = 'fail';\n\t\t\t\t\t\tcurrentTrace.error = err instanceof Error ? err.message : String(err);\n\t\t\t\t\t\tcurrentTrace.duration = Date.now() - startTime;\n\t\t\t\t\t\ttraceManager.addTrace(currentTrace);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 8) send the page to browser\n\n\t\t\tdebug('invokeProcedure: send the page to browser');\n\t\t\tawait sendResponse(req, res, pageComponents);\n\n\t\t\tif (traceData?.downloads) {\n\t\t\t\ttraceData.status = 'success';\n\t\t\t\ttraceData.duration = Date.now() - startTime;\n\t\t\t\ttraceManager.addTrace(traceData);\n\t\t\t}\n\t\t} finally {\n\t\t\t// 9) cleanup\n\n\t\t\tdebug('invokeProcedure: cleanup');\n\t\t\tfileBlob.destroy();\n\t\t}\n\t} catch (err) {\n\t\tif (traceData) {\n\t\t\ttraceData.status = err instanceof ProcedureError ? 'error' : 'fail';\n\t\t\ttraceData.error = err instanceof Error ? err.message : String(err);\n\t\t\ttraceData.duration = Date.now() - startTime;\n\t\t\ttraceManager.addTrace(traceData);\n\t\t}\n\t\tthrow err;\n\t}\n\n\tdebug('invokeProcedure: end');\n};","/*\n *\tPrepare the CGI information\n */\n\nimport os from 'node:os';\nimport {URL} from 'node:url';\nimport debugModule from 'debug';\nconst debug = debugModule('webplsql:cgi');\n\nimport type {Request} from 'express';\nimport type {environmentType} from '../../types.ts';\n\nconst DEFAULT_CGI: environmentType = {\n\tPLSQL_GATEWAY: 'WebDb',\n\tGATEWAY_IVERSION: '2',\n\tSERVER_SOFTWARE: 'web_plsql',\n\tGATEWAY_INTERFACE: 'CGI/1.1',\n\tSERVER_PORT: '',\n\tSERVER_NAME: os.hostname(),\n\tREQUEST_METHOD: '',\n\tPATH_INFO: '',\n\tSCRIPT_NAME: '',\n\tREMOTE_ADDR: '',\n\tSERVER_PROTOCOL: '',\n\tREQUEST_PROTOCOL: '',\n\tREMOTE_USER: '',\n\tHTTP_COOKIE: '',\n\tHTTP_USER_AGENT: '',\n\tHTTP_HOST: '',\n\tHTTP_ACCEPT: '',\n\tHTTP_ACCEPT_ENCODING: '',\n\tHTTP_ACCEPT_LANGUAGE: '',\n\tHTTP_REFERER: '',\n\tHTTP_X_FORWARDED_FOR: '',\n\tWEB_AUTHENT_PREFIX: '',\n\tDAD_NAME: '',\n\tDOC_ACCESS_PATH: 'doc',\n\tDOCUMENT_TABLE: '',\n\tPATH_ALIAS: '',\n\t// oxlint-disable-next-line unicorn/text-encoding-identifier-case\n\tREQUEST_CHARSET: 'UTF8',\n\t// oxlint-disable-next-line unicorn/text-encoding-identifier-case\n\tREQUEST_IANA_CHARSET: 'UTF-8',\n\tSCRIPT_PREFIX: '',\n};\n\n/**\n * Create the HTTP_COOKIE string\n *\n * @param req - The req object represents the HTTP request.\n * @returns The string representation of the cookies.\n */\nconst getCookieString = (req: Request): string => {\n\tlet cookieString = '';\n\n\tfor (const propName in req.cookies) {\n\t\tcookieString += `${propName}=${req.cookies[propName]};`;\n\t}\n\n\tdebug('getCookieString', req.cookies, cookieString);\n\n\treturn cookieString;\n};\n\n/**\n * Get the script name and DAD name from a URL\n *\n * @param req - The req object represents the HTTP request.\n * @returns The DAD structure.\n */\nconst getPath = (req: Request): {script: string; prefix: string; dad: string} => {\n\t// create a valid url\n\tconst validUrl = `${req.protocol}://${os.hostname()}${req.originalUrl}`;\n\n\t// get the pathname from the url (new URL('https://example.org/abc/xyz?123').pathname => /abc/xyz)\n\tconst pathname = new URL(validUrl).pathname;\n\n\tconst tmp = trimPath(pathname.slice(0, Math.max(0, pathname.lastIndexOf('/') + 1)));\n\tconst script = `/${tmp}`;\n\tconst prefix = `/${tmp.slice(0, Math.max(0, tmp.lastIndexOf('/')))}`;\n\tconst dad = tmp.slice(Math.max(0, tmp.indexOf('/') + 1));\n\n\treturn {script, prefix, dad};\n};\n\n/**\n * Get a path that is not enclodes in slashes\n *\n * @param value - The value to trim.\n * @returns The trimmed value.\n */\nconst trimPath = (value: string): string => value.replaceAll(/^\\/+|\\/+$/gu, '');\n\n/**\n * Create a CGI object\n *\n * @param req - The req object represents the HTTP request.\n * @param doctable - The document table.\n * @param cgi - The additional cgi.\n * @param authenticatedUser - The authenticated user.\n * @returns CGI object\n */\nexport const getCGI = (req: Request, doctable: string, cgi: environmentType, authenticatedUser: string | null = null): environmentType => {\n\tconst PROTOCOL = req.protocol ? req.protocol.toUpperCase() : '';\n\tconst PATH = getPath(req);\n\n\tconst CGI: environmentType = {\n\t\tSERVER_PORT: typeof req.socket.localPort === 'number' ? req.socket.localPort.toString() : '',\n\t\tREQUEST_METHOD: req.method,\n\t\tPATH_INFO: Array.isArray(req.params.name) ? (req.params.name[0] ?? '') : (req.params.name ?? ''),\n\t\tSCRIPT_NAME: PATH.script,\n\t\tREMOTE_ADDR: (req.ip ?? '').replace('::ffff:', ''),\n\t\tSERVER_PROTOCOL: `${PROTOCOL}/${req.httpVersion}`,\n\t\tREQUEST_PROTOCOL: PROTOCOL,\n\t\tREMOTE_USER: authenticatedUser ?? '',\n\t\tAUTH_TYPE: authenticatedUser ? 'Basic' : '',\n\t\tHTTP_COOKIE: getCookieString(req),\n\t\tHTTP_USER_AGENT: req.get('user-agent') ?? '',\n\t\tHTTP_HOST: req.get('host') ?? '',\n\t\tHTTP_ACCEPT: req.get('accept') ?? '',\n\t\tHTTP_ACCEPT_ENCODING: req.get('accept-encoding') ?? '',\n\t\tHTTP_ACCEPT_LANGUAGE: req.get('accept-language') ?? '',\n\t\tHTTP_REFERER: req.get('referer') ?? '',\n\t\tHTTP_X_FORWARDED_FOR: req.get('x-forwarded-for') ?? '',\n\t\tDAD_NAME: PATH.dad,\n\t\tDOCUMENT_TABLE: doctable,\n\t\tSCRIPT_PREFIX: PATH.prefix,\n\t};\n\n\treturn Object.assign({}, DEFAULT_CGI, CGI, cgi);\n};","/**\n *\tIs the given value a string or an array of strings\n *\t@param value - The value to check.\n *\t@returns True if the value is a string or an array of strings\n */\nexport const isStringOrArrayOfString = (value: unknown): value is string | string[] =>\n\ttypeof value === 'string' || (Array.isArray(value) && value.every((element) => typeof element === 'string'));","/*\n *\tProcess the http request\n */\n\nimport debugModule from 'debug';\nconst debug = debugModule('webplsql:request');\n\nimport util from 'node:util';\nimport {invokeProcedure} from './procedure.ts';\nimport {getCGI} from './cgi.ts';\nimport {getFiles} from './upload.ts';\nimport {RequestError} from './requestError.ts';\nimport {isStringOrArrayOfString} from '../../util/type.ts';\nimport type {Request, Response} from 'express';\nimport type {Pool} from 'oracledb';\nimport type {argObjType, configPlSqlHandlerType, ProcedureNameCache, ArgumentCache} from '../../types.ts';\n\n/**\n *\tNormalize the body by making sure that only \"simple\" parameters and no nested objects are submitted\n *\t@param req - The req object represents the HTTP request.\n *\t@returns The normalized body.\n */\nconst normalizeBody = (req: Request): Record<string, string | string[]> => {\n\tconst args: Record<string, string | string[]> = {};\n\n\t/* v8 ignore else - body validation */\n\tif (typeof req.body === 'object' && req.body !== null) {\n\t\tconst body = req.body as Record<string, unknown>;\n\t\tfor (const key in body) {\n\t\t\tconst value = body[key];\n\t\t\t/* v8 ignore else - type validation */\n\t\t\tif (isStringOrArrayOfString(value)) {\n\t\t\t\targs[key] = value;\n\t\t\t} else {\n\t\t\t\t/* v8 ignore next - invalid body type */\n\t\t\t\tthrow new RequestError(\n\t\t\t\t\t`The element \"${key}\" in the body is not a string or an array of strings!\\n${util.inspect(req.body, {showHidden: false, depth: null, colors: false})}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn args;\n};\n\n/**\n * Execute the request\n *\n * @param req - The req object represents the HTTP request.\n * @param res - The res object represents the HTTP response that an Express app sends when it gets an HTTP request.\n * @param options - the options for the middleware.\n * @param connectionPool - The connection pool.\n * @param procedureNameCache - The procedure name cache.\n * @param argumentCache - The argument cache.\n * @param authenticatedUser - The authenticated user identity.\n * @returns Promise resolving to th page\n */\nexport const processRequest = async (\n\treq: Request,\n\tres: Response,\n\toptions: configPlSqlHandlerType,\n\tconnectionPool: Pool,\n\tprocedureNameCache: ProcedureNameCache,\n\targumentCache: ArgumentCache,\n\tauthenticatedUser: string | null = null,\n): Promise<void> => {\n\tdebug('processRequest: ENTER');\n\n\tif (typeof req.params.name !== 'string') {\n\t\t// FIXME: this should be standartized\n\t\tconsole.warn(`processRequest: WARNING: the req.params.name is not a string but an array of string: ${req.params.name}`);\n\t}\n\n\t// open database connection\n\tconst connection = await connectionPool.getConnection();\n\n\ttry {\n\t\t// Get the CGI\n\t\tconst cgiObj = getCGI(req, options.documentTable, options.cgi ?? {}, authenticatedUser);\n\t\tdebug('processRequest: cgiObj=', cgiObj);\n\n\t\t// Does the request contain any files\n\t\tconst filesToUpload = getFiles(req);\n\t\tdebug('processRequest: filesToUpload=', filesToUpload);\n\n\t\t// Add the query properties\n\t\tconst argObj: argObjType = {};\n\t\t// oxlint-disable-next-line typescript/no-explicit-any, unicorn/no-immediate-mutation\n\t\tObject.assign(argObj, req.query as any);\n\n\t\t// For add the files that must be uploaded, we now copy the actual filename to the appropriate parameter to the invoked procedure.\n\t\tfilesToUpload.reduce((aggregator, file) => {\n\t\t\taggregator[file.fieldname] = file.filename;\n\t\t\treturn aggregator;\n\t\t}, argObj);\n\n\t\t// Does the request contain a body\n\t\tObject.assign(argObj, normalizeBody(req));\n\t\tdebug('processRequest: argObj=', argObj);\n\n\t\t// invoke the Oracle procedure and get the page contenst\n\t\tawait invokeProcedure(req, res, argObj, cgiObj, filesToUpload, options, connection, procedureNameCache, argumentCache);\n\n\t\t// transaction mode\n\t\tif (options.transactionMode === 'rollback') {\n\t\t\tdebug('transactionMode: rollback');\n\t\t\tawait connection.rollback();\n\t\t} else if (typeof options.transactionMode === 'function') {\n\t\t\tdebug('transactionMode: callback');\n\t\t\tconst procName = Array.isArray(req.params.name) ? req.params.name[0] : req.params.name;\n\t\t\tconst result = options.transactionMode(connection, procName ?? '');\n\t\t\tdebug('transactionMode: callback restult', result);\n\t\t\tif (result && typeof result.then === 'function') {\n\t\t\t\tawait result;\n\t\t\t}\n\t\t} else {\n\t\t\tdebug('transactionMode: commit');\n\t\t\tawait connection.commit();\n\t\t}\n\t} finally {\n\t\t// close database connection\n\t\tawait connection.release();\n\t}\n\n\tdebug('processRequest: EXIT');\n};","import * as rotatingFileStream from 'rotating-file-stream';\nimport {JSON_LOG_ROTATION_SIZE, JSON_LOG_ROTATION_INTERVAL, JSON_LOG_MAX_ROTATED_FILES} from '../../common/constants.ts';\nimport {type logEntryType} from '../types.ts';\nimport {type MakeOptional} from '../../common/typeUtilities.ts';\n\nexport class JsonLogger {\n\tstream: rotatingFileStream.RotatingFileStream;\n\n\tconstructor(filename = 'error.json.log') {\n\t\tthis.stream = rotatingFileStream.createStream(filename, {\n\t\t\tsize: JSON_LOG_ROTATION_SIZE, // rotate every 10 MegaBytes written\n\t\t\tinterval: JSON_LOG_ROTATION_INTERVAL, // rotate daily\n\t\t\tmaxFiles: JSON_LOG_MAX_ROTATED_FILES, // maximum number of rotated files to keep\n\t\t\tcompress: 'gzip', // compress rotated files\n\t\t});\n\t}\n\n\t/**\n\t * Log an entry as NDJSON.\n\t * @param entry - The entry to log.\n\t */\n\tlog(entry: MakeOptional<logEntryType, 'timestamp'>): void {\n\t\ttry {\n\t\t\t// Ensure timestamp exists\n\t\t\tentry.timestamp ??= new Date().toISOString();\n\t\t\tconst line = JSON.stringify(entry);\n\t\t\tthis.stream.write(line + '\\n');\n\t\t} catch (err) {\n\t\t\tconsole.error('JsonLogger: Failed to write log', err);\n\t\t}\n\t}\n\n\t/**\n\t * Close the stream.\n\t */\n\tclose(): void {\n\t\tthis.stream.end();\n\t}\n}\n\nexport const jsonLogger = new JsonLogger();","/*\n *\tError handling\n */\n\nimport {ProcedureError} from './procedureError.ts';\nimport {RequestError} from './requestError.ts';\nimport {getFormattedMessage, logToFile, type messageType} from '../../util/trace.ts';\nimport {errorToString} from '../../util/errorToString.ts';\nimport {getHtmlPage} from '../../util/html.ts';\nimport {jsonLogger} from '../../util/jsonLogger.ts';\nimport type {Request, Response} from 'express';\nimport type {environmentType, configPlSqlHandlerType} from '../../types.ts';\nimport type {BindParameters} from 'oracledb';\n\n/**\n *\tGet error data\n *\t@param req - The req object represents the HTTP request.\n *\t@param error - The error.\n *\t@returns The output.\n */\nconst getErrorData = (req: Request, error: unknown): messageType => {\n\tlet timestamp = new Date();\n\tlet message = '';\n\tlet environment: environmentType | null | undefined = null;\n\tlet sql: string | null | undefined = null;\n\tlet bind: BindParameters | null | undefined = null;\n\n\t// what type of Error did we receive\n\tif (error instanceof ProcedureError) {\n\t\ttimestamp = error.timestamp;\n\t\tmessage = error.stack ?? '';\n\t\tenvironment = error.environment;\n\t\tsql = error.sql;\n\t\tbind = error.bind;\n\t} else if (error instanceof RequestError) {\n\t\ttimestamp = error.timestamp;\n\t\tmessage = error.stack ?? '';\n\t} else if (error instanceof Error) {\n\t\tmessage = errorToString(error);\n\t} else {\n\t\tif (typeof error === 'string') {\n\t\t\tmessage = `${error}\\n`;\n\t\t}\n\t\t/* v8 ignore start - unreachable code: creating Error without throwing */\n\t\ttry {\n\t\t\t// oxlint-disable-next-line unicorn/error-message\n\t\t\tnew Error();\n\t\t} catch (err) {\n\t\t\tmessage += errorToString(err);\n\t\t}\n\t\t/* v8 ignore stop */\n\t}\n\n\treturn {type: 'error', timestamp, message, req, environment, sql, bind};\n};\n\n/**\n * Show an error page\n *\n * @param req - The req object represents the HTTP request.\n * @param res - The res object represents the HTTP response that an Express app sends when it gets an HTTP request.\n * @param options - The configuration options.\n * @param error - The error.\n */\nexport const errorPage = (req: Request, res: Response, options: configPlSqlHandlerType, error: unknown): void => {\n\t// get error data\n\tconst errorData = getErrorData(req, error);\n\n\t// get formatted message\n\tconst {html, text} = getFormattedMessage(errorData);\n\n\t// trace to file\n\tlogToFile(text);\n\n\t// json log\n\tconst firstLine = errorData.message.split('\\n')[0];\n\tjsonLogger.log({\n\t\ttimestamp: errorData.timestamp?.toISOString() ?? new Date().toISOString(),\n\t\ttype: 'error',\n\t\tmessage: firstLine ?? '',\n\t\treq: {\n\t\t\tmethod: req.method,\n\t\t\turl: req.originalUrl,\n\t\t\tip: req.ip ?? '',\n\t\t\tuserAgent: req.get('user-agent') ?? '',\n\t\t},\n\t\tdetails: {\n\t\t\tfullMessage: errorData.message,\n\t\t\tsql: errorData.sql ?? '',\n\t\t\tbind: errorData.bind,\n\t\t\tenvironment: errorData.environment ?? {},\n\t\t},\n\t});\n\n\t// console\n\tconsole.error(text);\n\n\t// If headers have already been sent, we cannot show the error page\n\tif (res.headersSent) {\n\t\tconsole.warn('errorPage: Response headers already sent. Cannot show error page.');\n\t\treturn;\n\t}\n\n\t// show page\n\tif (options.errorStyle === 'basic') {\n\t\tres.status(404).send('Page not found');\n\t} else {\n\t\tres.status(404).send(getHtmlPage(html));\n\t}\n};","/*\n *\tExpress middleware for Oracle PL/SQL\n */\n\nimport debugModule from 'debug';\nconst debug = debugModule('webplsql:handlerPlSql');\n\nimport {processRequest} from './request.ts';\nimport {RequestError} from './requestError.ts';\nimport {errorPage} from './errorPage.ts';\nimport {Cache} from '../../util/cache.ts';\nimport type {RequestHandler, Request, Response, NextFunction} from 'express';\nimport type {Pool} from 'oracledb';\nimport type {configPlSqlType, argsType} from '../../types.ts';\nimport type {AdminContext} from '../../server/adminContext.ts';\n\ntype WebPlSqlRequestHandler = RequestHandler & {\n\tprocedureNameCache: Cache<string>;\n\targumentCache: Cache<argsType>;\n};\n\n/**\n * express.Request handler\n * @param req - The req object represents the HTTP request.\n * @param res - The res object represents the HTTP response that an Express app sends when it gets an HTTP request.\n * @param _next - The next function.\n * @param connectionPool - The connection pool.\n * @param options - the options for the middleware.\n * @param procedureNameCache - The procedure name cache.\n * @param argumentCache - The argument cache.\n */\nconst requestHandler = async (\n\treq: Request,\n\tres: Response,\n\t_next: NextFunction,\n\tconnectionPool: Pool,\n\toptions: configPlSqlType,\n\tprocedureNameCache: Cache<string>,\n\targumentCache: Cache<argsType>,\n): Promise<void> => {\n\ttry {\n\t\tlet authenticatedUser: string | null = null;\n\n\t\t// authentication logic\n\t\tif (options.auth?.type === 'basic') {\n\t\t\tconst b64auth = (req.headers.authorization ?? '').split(' ')[1] ?? '';\n\t\t\tconst [login, password] = Buffer.from(b64auth, 'base64').toString().split(':');\n\n\t\t\tif (login) {\n\t\t\t\tauthenticatedUser = await options.auth.callback({username: login, password}, connectionPool);\n\t\t\t}\n\n\t\t\tif (authenticatedUser === null) {\n\t\t\t\tconst realm = options.auth.realm ?? 'PL/SQL Gateway';\n\t\t\t\tres.set('WWW-Authenticate', `Basic realm=\"${realm}\"`);\n\t\t\t\tres.status(401).send('Authentication required.');\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else if (options.auth?.type === 'custom') {\n\t\t\tauthenticatedUser = await options.auth.callback(req, connectionPool);\n\n\t\t\tif (authenticatedUser === null) {\n\t\t\t\tres.status(401).send('Authentication required.');\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// should we switch to the default page if there is one defined\n\t\tif (typeof req.params.name !== 'string' || req.params.name.length === 0) {\n\t\t\tif (typeof options.defaultPage === 'string' && options.defaultPage.length > 0) {\n\t\t\t\tconst currentUrl = new URL(req.originalUrl, 'http://localhost');\n\t\t\t\tconst basePath = currentUrl.pathname.endsWith('/') ? currentUrl.pathname : `${currentUrl.pathname}/`;\n\t\t\t\tconst defaultPage = options.defaultPage.replace(/^\\/+/u, '');\n\t\t\t\tconst newUrl = `${basePath}${defaultPage}${currentUrl.search}`;\n\t\t\t\tdebug(`Redirect to the url \"${newUrl}\"`);\n\t\t\t\tres.redirect(newUrl);\n\t\t\t} else {\n\t\t\t\terrorPage(req, res, options, new RequestError('No procedure name given and no default page has been specified'));\n\t\t\t}\n\t\t} else {\n\t\t\t// request handler\n\t\t\tawait processRequest(req, res, options, connectionPool, procedureNameCache, argumentCache, authenticatedUser);\n\t\t}\n\t} catch (err) {\n\t\terrorPage(req, res, options, err);\n\t}\n};\n\n/**\n * Express middleware.\n *\n * @param connectionPool - The connection pool.\n * @param config - The configuration options.\n * @param adminContext - Optional admin context for self-registration and stats tracking.\n * @returns The handler.\n */\nexport const handlerWebPlSql = (\n\tconnectionPool: Pool,\n\tconfig: configPlSqlType,\n\tadminContext?: AdminContext,\n): WebPlSqlRequestHandler & {procedureNameCache: Cache<string>; argumentCache: Cache<argsType>} => {\n\tdebug('options', config);\n\n\tconst procedureNameCache = new Cache<string>();\n\tconst argumentCache = new Cache<argsType>();\n\n\t// Self-register with AdminContext\n\tif (adminContext) {\n\t\tadminContext.registerHandler(config.route, connectionPool, procedureNameCache, argumentCache);\n\t}\n\n\t/**\n\t * @param req - The request.\n\t * @param res - The response.\n\t * @param next - The next function.\n\t */\n\tconst handler = ((req: Request, res: Response, next: NextFunction) => {\n\t\tvoid requestHandler(req, res, next, connectionPool, config, procedureNameCache, argumentCache);\n\t}) as WebPlSqlRequestHandler;\n\n\thandler.procedureNameCache = procedureNameCache;\n\thandler.argumentCache = argumentCache;\n\n\treturn handler;\n};","import debugModule from 'debug';\nconst debug = debugModule('webplsql:handlerLogger');\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport morgan from 'morgan';\nimport type {RequestHandler} from 'express';\n\n/**\n * Create the upload middleware.\n * @param filename - Output filename.\n * @returns Request handler.\n */\nexport const handlerLogger = (filename: string): RequestHandler => {\n\tdebug('register');\n\n\treturn morgan('combined', {stream: fs.createWriteStream(path.join(process.cwd(), filename), {flags: 'a'})});\n};","import multer from 'multer';\nimport type {RequestHandler} from 'express';\n\n/**\n * Create the upload middleware.\n * @param uploadFileSizeLimit - Maximum size of each uploaded file in bytes or no limit if omitted.\n * @returns Request handler.\n */\nexport const handlerUpload = (uploadFileSizeLimit?: number): RequestHandler => {\n\tconst upload = multer({\n\t\tstorage: multer.diskStorage({}),\n\t\tlimits: {\n\t\t\tfileSize: uploadFileSizeLimit,\n\t\t},\n\t});\n\n\treturn upload.any();\n};","import express, {type Request, type Response, type Router} from 'express';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport readline from 'node:readline';\nimport type {AdminContext} from '../server/adminContext.ts';\nimport {traceManager} from '../util/traceManager.ts';\nimport {getVersion} from '../version.ts';\nimport {SHUTDOWN_GRACE_DELAY_MS} from '../../common/constants.ts';\nimport {forceShutdown} from '../util/shutdown.ts';\nimport {logEntrySchema, procedureTraceEntrySchema} from '../types.ts';\nimport {z} from 'zod';\n\nconst version = () => getVersion();\n\n/**\n * Helper to read last N lines of a file\n * @param filePath - Path to file\n * @param n - Number of lines\n * @param filter - Optional filter string\n * @returns The lines\n */\nconst readLastLines = async (filePath: string, n = 100, filter = ''): Promise<string[]> => {\n\tif (!fs.existsSync(filePath)) {\n\t\treturn [];\n\t}\n\n\tconst fileStream = fs.createReadStream(filePath);\n\tconst rl = readline.createInterface({\n\t\tinput: fileStream,\n\t\tcrlfDelay: Infinity,\n\t});\n\n\tconst filterLower = filter.toLowerCase();\n\n\tconst lines: string[] = [];\n\tfor await (const line of rl) {\n\t\tif (!filter || line.toLowerCase().includes(filterLower)) {\n\t\t\tlines.push(line);\n\t\t\tif (lines.length > n) {\n\t\t\t\tlines.shift();\n\t\t\t}\n\t\t}\n\t}\n\treturn lines;\n};\n\n/**\n * Create admin API router\n * @param adminContext - The admin context\n * @returns Express router\n */\nexport const createAdminRouter = (adminContext: AdminContext): Router => {\n\tconst router = express.Router();\n\n\t// GET /api/status\n\trouter.get('/api/status', (req: Request, res: Response) => {\n\t\tconst uptime = (Date.now() - adminContext.startTime.getTime()) / 1000;\n\t\tconst includeHistory = req.query.history === 'true';\n\t\tconst includeConfig = req.query.config === 'true';\n\n\t\tconst poolStats = adminContext.pools.map((pool, index) => {\n\t\t\tconst cache = adminContext.caches[index];\n\t\t\tconst name = cache?.poolName ?? `pool-${index}`;\n\t\t\tconst p = pool as {getStatistics?: () => unknown};\n\t\t\tconst stats = typeof p.getStatistics === 'function' ? p.getStatistics() : null;\n\t\t\tconst procStats = cache?.procedureNameCache?.getStats();\n\t\t\tconst argStats = cache?.argumentCache?.getStats();\n\n\t\t\treturn {\n\t\t\t\tname,\n\t\t\t\tstats,\n\t\t\t\tconnectionsOpen: pool.connectionsOpen,\n\t\t\t\tconnectionsInUse: pool.connectionsInUse,\n\t\t\t\tcache: {\n\t\t\t\t\tprocedureName: {\n\t\t\t\t\t\tsize: cache?.procedureNameCache?.keys().length ?? 0,\n\t\t\t\t\t\thits: procStats?.hits ?? 0,\n\t\t\t\t\t\tmisses: procStats?.misses ?? 0,\n\t\t\t\t\t},\n\t\t\t\t\targument: {\n\t\t\t\t\t\tsize: cache?.argumentCache?.keys().length ?? 0,\n\t\t\t\t\t\thits: argStats?.hits ?? 0,\n\t\t\t\t\t\tmisses: argStats?.misses ?? 0,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t});\n\n\t\tconst memUsage = process.memoryUsage();\n\t\tconst systemMemoryUsed = os.totalmem() - os.freemem();\n\t\tconst cpuUsage = process.cpuUsage();\n\t\tconst summary = adminContext.statsManager.getSummary();\n\n\t\tconst history = adminContext.statsManager.getHistory();\n\t\t// If history is not requested, return only the last bucket for charts\n\t\tconst historyData = includeHistory ? history : history.slice(-1);\n\n\t\tres.json({\n\t\t\tversion: version(),\n\t\t\tstatus: adminContext.paused ? 'paused' : 'running',\n\t\t\tuptime,\n\t\t\tstartTime: adminContext.startTime,\n\t\t\tintervalMs: adminContext.statsManager.config.intervalMs,\n\t\t\tmetrics: {\n\t\t\t\trequestCount: summary.totalRequests,\n\t\t\t\terrorCount: summary.totalErrors,\n\t\t\t\tavgResponseTime: summary.avgResponseTime,\n\t\t\t\tminResponseTime: summary.minResponseTime,\n\t\t\t\tmaxResponseTime: summary.maxResponseTime,\n\t\t\t\tmaxRequestsPerSecond: summary.maxRequestsPerSecond,\n\t\t\t},\n\t\t\thistory: historyData,\n\t\t\tpools: poolStats,\n\t\t\tsystem: {\n\t\t\t\tnodeVersion: process.version,\n\t\t\t\tplatform: process.platform,\n\t\t\t\tarch: process.arch,\n\t\t\t\tcpuCores: os.cpus().length,\n\t\t\t\tmemory: {\n\t\t\t\t\trss: systemMemoryUsed,\n\t\t\t\t\theapTotal: memUsage.heapTotal,\n\t\t\t\t\theapUsed: memUsage.heapUsed,\n\t\t\t\t\texternal: memUsage.external,\n\t\t\t\t\ttotalMemory: os.totalmem(),\n\t\t\t\t\t...summary.maxMemory,\n\t\t\t\t},\n\t\t\t\tcpu: {\n\t\t\t\t\tuser: cpuUsage.user,\n\t\t\t\t\tsystem: cpuUsage.system,\n\t\t\t\t\tmax: summary.cpu.max,\n\t\t\t\t\tuserMax: summary.cpu.userMax,\n\t\t\t\t\tsystemMax: summary.cpu.systemMax,\n\t\t\t\t},\n\t\t\t},\n\t\t\tconfig:\n\t\t\t\tincludeConfig && adminContext.config\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...adminContext.config,\n\t\t\t\t\t\t\tadminPassword: adminContext.config.adminPassword ? '********' : undefined,\n\t\t\t\t\t\t\troutePlSql: adminContext.config.routePlSql.map((p) => {\n\t\t\t\t\t\t\t\tconst {auth, transactionMode, cgi, ...rest} = p;\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t...rest,\n\t\t\t\t\t\t\t\t\tpassword: '********',\n\t\t\t\t\t\t\t\t\thasAuth: !!auth,\n\t\t\t\t\t\t\t\t\thasTransactionMode: !!transactionMode,\n\t\t\t\t\t\t\t\t\thasCgi: !!cgi,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\t\t});\n\t});\n\n\t// GET /api/stats/history\n\trouter.get('/api/stats/history', (req: Request, res: Response) => {\n\t\tconst limitQuery = req.query.limit;\n\t\tlet limit = 100;\n\t\tif (typeof limitQuery === 'string') {\n\t\t\tconst parsed = Number(limitQuery);\n\t\t\tif (!Number.isNaN(parsed)) {\n\t\t\t\tlimit = parsed;\n\t\t\t}\n\t\t}\n\n\t\tconst history = adminContext.statsManager.getHistory();\n\t\t// Return the last 'limit' entries, reversed (newest first)\n\t\t// Create a copy to avoid mutating the original history array\n\t\tconst slice = limit > 0 ? history.slice(-limit) : [...history];\n\t\tres.json(slice.toReversed());\n\t});\n\n\t// GET /api/logs/error\n\trouter.get('/api/logs/error', async (req: Request, res: Response) => {\n\t\ttry {\n\t\t\tconst limit = Number(req.query.limit) || 100;\n\t\t\tconst filter = typeof req.query.filter === 'string' ? req.query.filter : '';\n\t\t\tconst logFile = 'error.json.log';\n\t\t\tconst lines = await readLastLines(logFile, limit, filter);\n\t\t\tconst parsedLines = lines\n\t\t\t\t.map((line) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn JSON.parse(line) as unknown;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.filter((l): l is unknown => l !== null);\n\t\t\tconst schema = z.array(logEntrySchema);\n\t\t\tconst logs = schema.safeParse(parsedLines);\n\t\t\tif (!logs.success) {\n\t\t\t\t// FIXME: this must be standardized\n\t\t\t\tthrow new Error(`Validation failed: ${logs.error.message}`);\n\t\t\t}\n\n\t\t\treturn res.json(logs.data.toReversed());\n\t\t} catch (err) {\n\t\t\treturn res.status(500).json({error: String(err)});\n\t\t}\n\t});\n\n\t// GET /api/logs/access\n\trouter.get('/api/logs/access', async (req: Request, res: Response) => {\n\t\ttry {\n\t\t\tconst limit = Number(req.query.limit) || 100;\n\t\t\tconst filter = typeof req.query.filter === 'string' ? req.query.filter : '';\n\t\t\tconst logFile = adminContext.config?.loggerFilename ?? 'access.log';\n\n\t\t\tif (!adminContext.config?.loggerFilename) {\n\t\t\t\tres.json({message: 'Access logging not enabled'});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst lines = await readLastLines(logFile, limit, filter);\n\t\t\tres.json(lines.toReversed());\n\t\t} catch (err) {\n\t\t\tres.status(500).json({error: String(err)});\n\t\t}\n\t});\n\n\t// POST /api/cache/clear\n\trouter.post('/api/cache/clear', (req: Request, res: Response) => {\n\t\tconst body = req.body as {poolName?: unknown} | null | undefined;\n\t\tconst poolName = typeof body?.poolName === 'string' ? body.poolName : undefined;\n\n\t\tlet cleared = 0;\n\t\tadminContext.caches.forEach((c) => {\n\t\t\tif (!poolName || c.poolName === poolName) {\n\t\t\t\tc.procedureNameCache.clear();\n\t\t\t\tcleared++;\n\t\t\t\tc.argumentCache.clear();\n\t\t\t\tcleared++;\n\t\t\t}\n\t\t});\n\n\t\tres.json({message: `Cleared ${cleared} caches`});\n\t});\n\n\t// POST /api/server/:action\n\trouter.post('/api/server/:action', (req: Request, res: Response) => {\n\t\tconst action = req.params.action;\n\n\t\tswitch (action) {\n\t\t\tcase 'stop': {\n\t\t\t\tres.json({message: 'Server shutting down...'});\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\tforceShutdown();\n\t\t\t\t}, SHUTDOWN_GRACE_DELAY_MS);\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'pause': {\n\t\t\t\tadminContext.setPaused(true);\n\t\t\t\tres.json({message: 'Server paused', status: 'paused'});\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'resume': {\n\t\t\t\tadminContext.setPaused(false);\n\t\t\t\tres.json({message: 'Server resumed', status: 'running'});\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tres.status(400).json({error: 'Invalid action'});\n\t\t\t}\n\t\t}\n\t});\n\n\t// GET /api/trace/status\n\trouter.get('/api/trace/status', (_req: Request, res: Response) => {\n\t\tres.json({enabled: traceManager.isEnabled()});\n\t});\n\n\t// POST /api/trace/toggle\n\trouter.post('/api/trace/toggle', (req: Request, res: Response) => {\n\t\tconst body = req.body as {enabled?: unknown} | null | undefined;\n\t\tconst enabled = typeof body?.enabled === 'boolean' ? body.enabled : false;\n\t\ttraceManager.setEnabled(enabled);\n\t\tres.json({enabled: traceManager.isEnabled()});\n\t});\n\n\t// GET /api/trace/logs\n\trouter.get('/api/trace/logs', async (req: Request, res: Response) => {\n\t\ttry {\n\t\t\tconst limit = Number(req.query.limit) || 100;\n\t\t\tconst filter = typeof req.query.filter === 'string' ? req.query.filter : '';\n\t\t\tconst logFile = traceManager.getFilePath();\n\t\t\tconst lines = await readLastLines(logFile, limit, filter);\n\t\t\tconst parsedLines = lines\n\t\t\t\t.map((line) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn JSON.parse(line) as unknown;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.filter((l): l is unknown => l !== null);\n\t\t\tconst schema = z.array(procedureTraceEntrySchema);\n\t\t\tconst logs = schema.safeParse(parsedLines);\n\t\t\tif (!logs.success) {\n\t\t\t\t// FIXME: this must be standardized\n\t\t\t\tthrow new Error(`Validation failed: ${logs.error.message}`);\n\t\t\t}\n\n\t\t\treturn res.json(logs.data.toReversed());\n\t\t} catch (err) {\n\t\t\treturn res.status(500).json({error: String(err)});\n\t\t}\n\t});\n\n\t// POST /api/trace/clear\n\trouter.post('/api/trace/clear', (_req: Request, res: Response) => {\n\t\ttraceManager.clear();\n\t\tres.json({message: 'Traces cleared'});\n\t});\n\n\treturn router;\n};","import {Router, type Request, type Response, type NextFunction, type RequestHandler} from 'express';\nimport {existsSync} from 'node:fs';\nimport path from 'node:path';\nimport expressStaticGzip from 'express-static-gzip';\nimport type {AdminContext} from '../server/adminContext.ts';\nimport type {PoolSnapshot} from '../util/statsManager.ts';\nimport {createAdminRouter} from './handlerAdmin.ts';\n\n/**\n * Resolves the admin console static directory by walking up from the current module\n * to find the project root (identified by package.json), then returns dist/frontend path.\n * @returns Path to dist/frontend directory\n * @throws {Error} if project root cannot be found\n */\nexport const resolveAdminStaticDir = (): string => {\n\tconst __dirname = import.meta.dirname;\n\n\tlet projectRoot = __dirname;\n\twhile (!existsSync(path.join(projectRoot, 'package.json')) && projectRoot !== '/') {\n\t\tprojectRoot = path.dirname(projectRoot);\n\t}\n\n\tif (projectRoot === '/') {\n\t\tthrow new Error('Could not find project root (package.json). Please provide explicit staticDir in AdminConsoleConfig.');\n\t}\n\n\treturn path.join(projectRoot, 'dist', 'frontend');\n};\n\nexport type AdminConsoleConfig = {\n\t/** Base route for the admin console (defaults to '/admin') */\n\tadminRoute?: string | undefined;\n\t/** Path to built admin frontend directory (optional - auto-detects if omitted) */\n\tstaticDir?: string | undefined;\n\t/** Optional username for basic auth */\n\tuser?: string | undefined;\n\t/** Optional password for basic auth */\n\tpassword?: string | undefined;\n\t/** Skip static dir validation (for dev mode) */\n\tdevMode?: boolean | undefined;\n};\n\n/**\n * Creates the admin console middleware.\n * @param config - The admin console configuration.\n * @param adminContext - The admin context.\n * @returns The express request handler.\n */\nexport const handlerAdminConsole = (config: AdminConsoleConfig, adminContext: AdminContext): RequestHandler => {\n\tconst adminRoute = config.adminRoute ?? '/admin';\n\tconst resolvedStaticDir = config.staticDir ?? resolveAdminStaticDir();\n\n\t// Validation\n\tif (adminRoute && !adminRoute.startsWith('/')) {\n\t\tthrow new Error('adminRoute must start with /');\n\t}\n\n\tif (!config.devMode && !existsSync(resolvedStaticDir)) {\n\t\tthrow new Error(`Admin console not built. Run 'npm run build:frontend' first.\\nExpected: ${resolvedStaticDir}`);\n\t}\n\n\t// StatsManager hook - ensure we only wrap once\n\tconst statsManager = adminContext.statsManager;\n\t// oxlint-disable-next-line typescript/unbound-method\n\tconst currentRotate = statsManager.rotateBucket;\n\tif (!Object.prototype.hasOwnProperty.call(currentRotate, '_isWrapped')) {\n\t\t// oxlint-disable-next-line typescript/unbound-method\n\t\tconst originalRotate = statsManager.rotateBucket;\n\t\tconst wrappedRotate = (poolSnapshots: PoolSnapshot[] = []) => {\n\t\t\tconst currentSnapshots: PoolSnapshot[] = adminContext.pools.map((pool, index) => {\n\t\t\t\tconst cache = adminContext.caches[index];\n\t\t\t\tconst name = cache?.poolName ?? `pool-${index}`;\n\t\t\t\tconst procStats = cache?.procedureNameCache?.getStats();\n\t\t\t\tconst argStats = cache?.argumentCache?.getStats();\n\n\t\t\t\treturn {\n\t\t\t\t\tname,\n\t\t\t\t\tconnectionsOpen: pool.connectionsOpen,\n\t\t\t\t\tconnectionsInUse: pool.connectionsInUse,\n\t\t\t\t\tcache: {\n\t\t\t\t\t\tprocedureName: {\n\t\t\t\t\t\t\tsize: cache?.procedureNameCache?.keys().length ?? 0,\n\t\t\t\t\t\t\thits: procStats?.hits ?? 0,\n\t\t\t\t\t\t\tmisses: procStats?.misses ?? 0,\n\t\t\t\t\t\t},\n\t\t\t\t\t\targument: {\n\t\t\t\t\t\t\tsize: cache?.argumentCache?.keys().length ?? 0,\n\t\t\t\t\t\t\thits: argStats?.hits ?? 0,\n\t\t\t\t\t\t\tmisses: argStats?.misses ?? 0,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t});\n\t\t\t// Merge snapshots if provided\n\t\t\tconst mergedSnapshots: PoolSnapshot[] = [...currentSnapshots];\n\t\t\tif (Array.isArray(poolSnapshots)) {\n\t\t\t\tfor (const ps of poolSnapshots) {\n\t\t\t\t\tif (!mergedSnapshots.some((s) => s.name === ps.name)) {\n\t\t\t\t\t\tmergedSnapshots.push(ps);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\toriginalRotate.call(statsManager, mergedSnapshots);\n\t\t};\n\t\tObject.defineProperty(wrappedRotate, '_isWrapped', {value: true, writable: false});\n\t\tstatsManager.rotateBucket = wrappedRotate;\n\t}\n\n\tconst router = Router();\n\n\t// Pause middleware\n\trouter.use((req: Request, res: Response, next: NextFunction) => {\n\t\tif (adminContext.paused && !req.path.startsWith(adminRoute)) {\n\t\t\tres.status(503).send('Server Paused');\n\t\t\treturn;\n\t\t}\n\t\tnext();\n\t});\n\n\t// Route filter - all following middleware only apply to adminRoute\n\trouter.use(adminRoute, (req: Request, res: Response, next: NextFunction) => {\n\t\t// Trailing slash redirect\n\t\tconst baseUrl = req.baseUrl || '';\n\t\tconst [path] = req.originalUrl.split('?');\n\n\t\tif (path === baseUrl) {\n\t\t\tconst query = req.originalUrl.split('?')[1];\n\t\t\treturn res.redirect(baseUrl + '/' + (query ? '?' + query : ''));\n\t\t}\n\t\tnext();\n\t});\n\n\t// Auth middleware\n\tif (config.user && config.password) {\n\t\trouter.use(adminRoute, (req: Request, res: Response, next: NextFunction) => {\n\t\t\tconst b64auth = (req.headers.authorization ?? '').split(' ')[1] ?? '';\n\t\t\tconst [login, password] = Buffer.from(b64auth, 'base64').toString().split(':');\n\n\t\t\tif (login !== config.user || password !== config.password) {\n\t\t\t\tres.set('WWW-Authenticate', 'Basic realm=\"Admin Console\"');\n\t\t\t\tres.status(401).send('Authentication required.');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tnext();\n\t\t});\n\t}\n\n\t// Mount handlerAdmin API routes\n\trouter.use(adminRoute, createAdminRouter(adminContext));\n\n\t// Mount static files\n\tif (existsSync(resolvedStaticDir)) {\n\t\trouter.use(\n\t\t\tadminRoute,\n\t\t\texpressStaticGzip(resolvedStaticDir, {\n\t\t\t\tenableBrotli: true,\n\t\t\t\torderPreference: ['br'],\n\t\t\t}),\n\t\t);\n\t}\n\n\treturn router;\n};","import path from 'node:path';\nimport {type Request, type Response, type NextFunction, type RequestHandler} from 'express';\n\n/**\n * Creates middleware that serves index.html for SPA routes.\n *\n * Handles React Router (createBrowserRouter), Vue Router, Angular Router, etc.\n *\n * **CRITICAL**: This middleware MUST be mounted AFTER express-static-gzip.\n *\n * Middleware order:\n * 1. express-static-gzip → Serves actual files (CSS, JS, images)\n * 2. handlerSpaFallback → Serves index.html for navigation requests\n *\n * @param directoryPath - Path to the static directory containing index.html\n * @param _route - The route prefix (for debugging)\n * @returns Express request handler\n *\n * @example\n * ```typescript\n * app.use('/app', expressStaticGzip('./build'));\n * app.use('/app', createSpaFallback('./build', '/app'));\n * ```\n */\nexport const createSpaFallback = (directoryPath: string, _route: string): RequestHandler => {\n\tconst indexPath = path.join(directoryPath, 'index.html');\n\n\treturn (req: Request, res: Response, next: NextFunction): void => {\n\t\t// Only handle GET/HEAD requests (standard browser navigation)\n\t\tif (req.method !== 'GET' && req.method !== 'HEAD') {\n\t\t\treturn next();\n\t\t}\n\n\t\t// Check Accept header to avoid serving HTML for API/asset requests\n\t\tconst accept = req.headers.accept ?? '';\n\n\t\t// Skip if Accept header explicitly excludes HTML\n\t\t// Handles: Accept: application/json, image/*, text/css, etc.\n\t\tif (accept && !accept.includes('text/html') && !accept.includes('*/*')) {\n\t\t\treturn next();\n\t\t}\n\n\t\t// Send index.html for navigation requests\n\t\tres.sendFile(indexPath, (err) => {\n\t\t\tif (err) {\n\t\t\t\t// index.html missing - let Express handle 404\n\t\t\t\tnext(err);\n\t\t\t}\n\t\t});\n\t};\n};","import process from 'node:process';\nimport chalk from 'chalk';\n\n// Named aliases — chalk 16-color approximations, original ansi256 noted\nconst C = {\n\tred: chalk.ansi256(196), // chalk.redBright.bold\n\tgray: chalk.gray, // ansi256(245)\n\tdimGray: chalk.blackBright, // ansi256(240)\n\twhite: chalk.whiteBright, // ansi256(255)\n} as const;\n\n/**\n * Prints a formatted CLI error block to stderr with timestamp.\n *\n * @param message - Primary error description\n * @param meta - Optional metadata\n */\nexport const printError = (message: string, meta?: Record<string, string>): void => {\n\tconst ts = new Date()\n\t\t.toISOString()\n\t\t.replace('T', ' ')\n\t\t.replace(/(\\.\\d{3})Z$/u, '.$1 UTC');\n\n\tconst sep = C.dimGray('─'.repeat(48));\n\tconst header = `${C.red('✖ ERROR')}  ${C.dimGray(ts)}`;\n\tconst body = C.white(message);\n\n\tconst defaultMeta: Record<string, string> = {\n\t\tpid: String(process.pid),\n\t\tnode: process.version,\n\t\t...meta,\n\t};\n\n\tconst footer = Object.entries(defaultMeta)\n\t\t.map(([k, v]) => `${C.dimGray(k)} ${C.gray(v)}`)\n\t\t.join('  ');\n\n\tconsole.error([sep, header, body, sep, footer].join('\\n'));\n};","/*\n *\tError handling\n */\n\nimport {logToFile} from './trace.ts';\nimport {printError} from './printError.ts';\nimport {errorToString} from './errorToString.ts';\nimport {jsonLogger} from './jsonLogger.ts';\n\n/**\n * Log an error.\n *\n * @param error - The error.\n */\nexport const logError = (error: unknown): void => {\n\tlet message = '';\n\n\t// what type of Error did we receive\n\tif (error instanceof Error) {\n\t\tmessage = errorToString(error);\n\t} else {\n\t\tif (typeof error === 'string') {\n\t\t\tmessage = error;\n\t\t}\n\t\t/* v8 ignore start - unreachable code: creating Error without throwing */\n\t\ttry {\n\t\t\t// oxlint-disable-next-line unicorn/error-message\n\t\t\tnew Error();\n\t\t} catch (err) {\n\t\t\tmessage += errorToString(err);\n\t\t}\n\t\t/* v8 ignore stop */\n\t}\n\n\t// trace to file\n\tlogToFile(message);\n\n\t// json log\n\tjsonLogger.log({\n\t\ttimestamp: new Date().toISOString(),\n\t\ttype: 'error',\n\t\tmessage,\n\t});\n\n\t// print error\n\tprintError(message);\n};"],"mappings":"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"}