UNPKG

web_plsql

Version:

The Express Middleware for Oracle PL/SQL

646 lines (645 loc) 20.9 kB
import z$1, { z } from "zod"; import oracledb, { BindParameter, BindParameters, BindParameters as BindParameters$1, Connection, Connection as Connection$1, DbType, ExecuteOptions, Lob, Pool, Pool as Pool$1, Result, Result as Result$1 } from "oracledb"; import http from "node:http"; import https from "node:https"; import { Express, RequestHandler, Router } from "express"; import { Readable } from "node:stream"; //#region src/backend/util/cache.d.ts type cacheEntryType<T> = { hitCount: number; value: T; }; /** * Generic Cache class with LFU (Least Frequently Used) eviction policy. */ declare class Cache<T> { cache: Map<string, cacheEntryType<T>>; maxSize: number; hits: number; misses: number; /** * @param maxSize - Maximum number of entries in the cache. */ constructor(maxSize?: number); /** * Get an entry from the cache. * @param key - The key. * @returns The value or undefined if not found. */ get(key: string): T | undefined; /** * Set an entry in the cache. * @param key - The key. * @param value - The value. */ set(key: string, value: T): void; /** * Delete an entry from the cache. * @param key - The key. */ delete(key: string): void; /** * Clear the cache. */ clear(): void; /** * Prune the cache by removing the least frequently used entries. * Removes 10% of the cache size. */ prune(): void; /** * Get the size of the cache. * @returns The size. */ get size(): number; /** * Get all keys in the cache. * @returns The keys. */ keys(): string[]; /** * Get cache statistics. * @returns The statistics. */ getStats(): { size: number; maxSize: number; hits: number; misses: number; }; } //#endregion //#region src/backend/types.d.ts /** * Custom callback signature for manual transaction handling */ type transactionCallbackType = (connection: Connection, procedure: string) => void | Promise<void>; /** * Defines how transactions are handled after procedure execution * 'commit': automatically commit * 'rollback': automatically rollback * callback: custom function for manual handling */ /** * Authentication callback signature. * Returns the identity string on success, or null on failure. * @public */ type AuthCallback = (connectionPool: Pool, credentials: { username: string; password?: string | undefined; }) => Promise<string | null>; /** * PL/SQL handler behavior configuration */ declare const z$configPlSqlHandlerType: z$1.ZodObject<{ /** Default procedure to execute if none specified */defaultPage: z$1.ZodString; /** Virtual path alias for procedures */ pathAlias: z$1.ZodOptional<z$1.ZodString>; /** Procedure name associated with the path alias */ pathAliasProcedure: z$1.ZodOptional<z$1.ZodString>; /** Database table used for file uploads/downloads */ documentTable: z$1.ZodString; /** List of pattern/procedure names excluded from execution */ exclusionList: z$1.ZodOptional<z$1.ZodArray<z$1.ZodString>>; /** PL/SQL function called to validate requests */ requestValidationFunction: z$1.ZodOptional<z$1.ZodString>; /** Post-execution transaction behavior */ transactionMode: z$1.ZodOptional<z$1.ZodUnion<readonly [z$1.ZodCustom<transactionCallbackType, transactionCallbackType>, z$1.ZodLiteral<"commit">, z$1.ZodLiteral<"rollback">, z$1.ZodUndefined, z$1.ZodNull]>>; /** Error reporting style */ errorStyle: z$1.ZodEnum<{ basic: "basic"; debug: "debug"; }>; /** Static CGI environment variables to be passed to the session */ cgi: z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodString>>; /** Authentication settings */ auth: z$1.ZodOptional<z$1.ZodObject<{ /** Authentication type */type: z$1.ZodLiteral<"basic">; /** Callback function to validate credentials */ callback: z$1.ZodCustom<AuthCallback, AuthCallback>; /** Authentication realm */ realm: z$1.ZodOptional<z$1.ZodString>; }, z$1.core.$strict>>; }, z$1.core.$strict>; type configPlSqlHandlerType = z$1.infer<typeof z$configPlSqlHandlerType>; /** * Database connection configuration for a PL/SQL route */ declare const z$configPlSqlConfigType: z$1.ZodObject<{ /** URL route prefix for this database connection */route: z$1.ZodString; /** Database username */ user: z$1.ZodString; /** Database password */ password: z$1.ZodString; /** Oracle connection string (TNS or EZConnect) */ connectString: z$1.ZodString; }, z$1.core.$strict>; type configPlSqlConfigType = z$1.infer<typeof z$configPlSqlConfigType>; /** * Complete PL/SQL route configuration combining handler and connection settings */ type configPlSqlType = configPlSqlHandlerType & configPlSqlConfigType; /** * Root application configuration */ declare const z$configType: z$1.ZodObject<{ /** Server listening port */port: z$1.ZodNumber; /** Array of static file routes */ routeStatic: z$1.ZodArray<z$1.ZodObject<{ route: z$1.ZodString; directoryPath: z$1.ZodString; spaFallback: z$1.ZodOptional<z$1.ZodBoolean>; }, z$1.core.$strict>>; /** Array of PL/SQL routes */ routePlSql: z$1.ZodArray<z$1.ZodObject<{ /** URL route prefix for this database connection */route: z$1.ZodString; /** Database username */ user: z$1.ZodString; /** Database password */ password: z$1.ZodString; /** Oracle connection string (TNS or EZConnect) */ connectString: z$1.ZodString; /** Default procedure to execute if none specified */ defaultPage: z$1.ZodString; /** Virtual path alias for procedures */ pathAlias: z$1.ZodOptional<z$1.ZodString>; /** Procedure name associated with the path alias */ pathAliasProcedure: z$1.ZodOptional<z$1.ZodString>; /** Database table used for file uploads/downloads */ documentTable: z$1.ZodString; /** List of pattern/procedure names excluded from execution */ exclusionList: z$1.ZodOptional<z$1.ZodArray<z$1.ZodString>>; /** PL/SQL function called to validate requests */ requestValidationFunction: z$1.ZodOptional<z$1.ZodString>; /** Post-execution transaction behavior */ transactionMode: z$1.ZodOptional<z$1.ZodUnion<readonly [z$1.ZodCustom<transactionCallbackType, transactionCallbackType>, z$1.ZodLiteral<"commit">, z$1.ZodLiteral<"rollback">, z$1.ZodUndefined, z$1.ZodNull]>>; /** Error reporting style */ errorStyle: z$1.ZodEnum<{ basic: "basic"; debug: "debug"; }>; /** Static CGI environment variables to be passed to the session */ cgi: z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodString>>; /** Authentication settings */ auth: z$1.ZodOptional<z$1.ZodObject<{ /** Authentication type */type: z$1.ZodLiteral<"basic">; /** Callback function to validate credentials */ callback: z$1.ZodCustom<AuthCallback, AuthCallback>; /** Authentication realm */ realm: z$1.ZodOptional<z$1.ZodString>; }, z$1.core.$strict>>; }, z$1.core.$strict>>; /** Maximum allowed size for file uploads (bytes) */ uploadFileSizeLimit: z$1.ZodOptional<z$1.ZodNumber>; /** Path to the log file */ loggerFilename: z$1.ZodString; /** URL route prefix for the admin console */ adminRoute: z$1.ZodOptional<z$1.ZodString>; /** Username for admin console authentication */ adminUser: z$1.ZodOptional<z$1.ZodString>; /** Password for admin console authentication */ adminPassword: z$1.ZodOptional<z$1.ZodString>; /** Developer mode (skips frontend build check, enables CORS) */ devMode: z$1.ZodOptional<z$1.ZodBoolean>; }, z$1.core.$strict>; type configType = z$1.infer<typeof z$configType>; /** * Mapping of PL/SQL procedure argument names to their database types */ type argsType = Record<string, string>; //#endregion //#region src/backend/util/oracledb-mock.d.ts type ExecuteCallback = (sql: string, binds?: BindParameters) => Promise<Result<unknown>> | Result<unknown>; /** * Test utility: Set the callback for execute calls. * @param cb - The callback */ declare const setExecuteCallback: (cb: ExecuteCallback | null) => void; declare namespace oracledb_provider_d_exports { export { BIND_IN, BIND_INOUT, BIND_OUT, BLOB, BUFFER, BindParameter, BindParameters$1 as BindParameters, CLOB, CURSOR, Connection$1 as Connection, DATE, DB_TYPE_CLOB, DB_TYPE_DATE, DB_TYPE_NUMBER, DB_TYPE_VARCHAR, DbType, ExecuteCallback, ExecuteOptions, Lob, NUMBER, Pool$1 as Pool, Result$1 as Result, STRING, createPool, setExecuteCallback }; } /** * Create a database pool. * @param config - The pool attributes. * @returns The pool. */ declare function createPool(config: oracledb.PoolAttributes): Promise<oracledb.Pool>; declare const BIND_IN: number; declare const BIND_OUT: number; declare const BIND_INOUT: number; declare const STRING: oracledb.DbType & { num: 2001; name: "DB_TYPE_VARCHAR"; columnTypeName: "VARCHAR2"; oraTypeNum: 1; bufferSizeFactor: 4; csfrm: oracledb.CSFRM_IMPLICIT; }; declare const NUMBER: oracledb.DbType & { num: 2010; name: "DB_TYPE_NUMBER"; columnTypeName: "NUMBER"; oraTypeNum: 2; bufferSizeFactor: 22; }; declare const DATE: oracledb.DbType & { num: 2012; name: "DB_TYPE_TIMESTAMP"; columnTypeName: "TIMESTAMP"; oraTypeNum: 180; bufferSizeFactor: 11; }; declare const CURSOR: oracledb.DbType & { num: 2021; name: "DB_TYPE_CURSOR"; columnTypeName: "CURSOR"; oraTypeNum: 102; bufferSizeFactor: 4; }; declare const BUFFER: oracledb.DbType & { num: 2006; name: "DB_TYPE_RAW"; columnTypeName: "RAW"; oraTypeNum: 23; bufferSizeFactor: 1; }; declare const CLOB: oracledb.DbType & { num: 2017; name: "DB_TYPE_CLOB"; columnTypeName: "CLOB"; oraTypeNum: 112; bufferSizeFactor: 112; csfrm: oracledb.CSFRM_IMPLICIT; }; declare const BLOB: oracledb.DbType & { num: 2019; name: "DB_TYPE_BLOB"; columnTypeName: "BLOB"; oraTypeNum: 113; bufferSizeFactor: 112; }; declare const DB_TYPE_VARCHAR: oracledb.DbType & { num: 2001; name: "DB_TYPE_VARCHAR"; columnTypeName: "VARCHAR2"; oraTypeNum: 1; bufferSizeFactor: 4; csfrm: oracledb.CSFRM_IMPLICIT; }; declare const DB_TYPE_CLOB: oracledb.DbType & { num: 2017; name: "DB_TYPE_CLOB"; columnTypeName: "CLOB"; oraTypeNum: 112; bufferSizeFactor: 112; csfrm: oracledb.CSFRM_IMPLICIT; }; declare const DB_TYPE_NUMBER: oracledb.DbType & { num: 2010; name: "DB_TYPE_NUMBER"; columnTypeName: "NUMBER"; oraTypeNum: 2; bufferSizeFactor: 22; }; declare const DB_TYPE_DATE: oracledb.DbType & { num: 2011; name: "DB_TYPE_DATE"; columnTypeName: "DATE"; oraTypeNum: 12; bufferSizeFactor: 7; }; //#endregion //#region src/backend/version.d.ts declare global { var __VERSION__: string; } /** * Returns the current library version * @returns {string} - Version. */ declare const getVersion: () => string; //#endregion //#region src/backend/server/config.d.ts /** * Show configuration. * @param config - The config. */ declare const showConfig: (config: configType) => void; //#endregion //#region src/backend/server/server.d.ts type webServer = { config: configType; connectionPools: Pool[]; app: Express; server: http.Server | https.Server; adminContext: AdminContext; shutdown: () => Promise<void>; }; type sslConfig = { keyFilename: string; certFilename: string; }; /** * Create HTTPS server. * @param app - express application * @param ssl - ssl configuration. * @returns server */ declare const createServer: (app: Express, ssl?: sslConfig) => http.Server | https.Server; /** * Start server. * @param config - The config. * @param ssl - ssl configuration. * @returns Promise resolving to the web server object. */ declare const startServer: (config: configType, ssl?: sslConfig) => Promise<webServer>; /** * Load configuration. * @param filename - The configuration filename. * @returns Promise. */ declare const loadConfig: (filename?: string) => configType; /** * Start server from config file. * @param filename - The configuration filename. * @param ssl - ssl configuration. * @returns Promise resolving to the web server object. */ declare const startServerConfig: (filename?: string, ssl?: sslConfig) => Promise<webServer>; //#endregion //#region src/backend/util/shutdown.d.ts /** * Install a shutdown handler. * @param handler - Shutdown handler */ declare const installShutdown: (handler: () => Promise<void>) => void; /** * Force a shutdown. */ declare const forceShutdown: () => void; //#endregion //#region src/backend/util/statsManager.d.ts type StatsConfig = { intervalMs: number; maxHistoryPoints: number; sampleSystem: boolean; samplePools: boolean; percentilePrecision: number; }; type CacheStats = { size: number; hits: number; misses: number; }; type PoolCacheSnapshot = { procedureName: CacheStats; argument: CacheStats; }; type PoolSnapshot = { name: string; connectionsInUse: number; connectionsOpen: number; cache?: PoolCacheSnapshot; }; type Bucket = { timestamp: number; requests: number; errors: number; durationMin: number; durationMax: number; durationAvg: number; durationP95: number; durationP99: number; system: { cpu: number; heapUsed: number; heapTotal: number; rss: number; external: number; }; pools: PoolSnapshot[]; }; type CurrentBucket = { count: number; errors: number; durationSum: number; durationMin: number; durationMax: number; durations: number[]; }; type MemoryLifetime = { heapUsedMax: number; heapTotalMax: number; rssMax: number; externalMax: number; }; type LifetimeStats = { totalRequests: number; totalErrors: number; minDuration: number; maxDuration: number; totalDuration: number; maxRequestsPerSecond: number; memory: MemoryLifetime; cpu: { max: number; userMax: number; systemMax: number; }; }; type StatsSummary = { startTime: Date; totalRequests: number; totalErrors: number; avgResponseTime: number; minResponseTime: number; maxResponseTime: number; maxRequestsPerSecond: number; maxMemory: MemoryLifetime; cpu: { max: number; userMax: number; systemMax: number; }; }; /** * Manager for statistical data collection and temporal bucketing. */ declare class StatsManager { config: StatsConfig; startTime: Date; history: Bucket[]; lifetime: LifetimeStats; _currentBucket: CurrentBucket; _lastCpuTimes: { user: number; nice: number; sys: number; idle: number; irq: number; total: number; }; _timer: NodeJS.Timeout | undefined; /** * @param config - Configuration. */ constructor(config?: Partial<StatsConfig>); /** * Reset the current bucket accumulator. */ private _resetBucket; /** * Record a request event. * @param duration - Duration in milliseconds. * @param isError - Whether the request was an error. */ recordRequest(duration: number, isError?: boolean): void; /** * Get system CPU times. * @returns System CPU times. */ private _getSystemCpuTimes; /** * Calculate CPU usage percentage since last call. * @returns CPU usage percentage (0-100). */ private _calculateCpuUsage; /** * Rotate the current bucket into history and start a new one. * @param poolSnapshots - Optional pool snapshots to include. */ rotateBucket(poolSnapshots?: PoolSnapshot[]): void; /** * Stop the background timer. */ stop(): void; /** * Get lifetime summary. * @returns Summary. */ getSummary(): StatsSummary; /** * Get history buffer. * @returns The history buffer. */ getHistory(): Bucket[]; } //#endregion //#region src/backend/server/adminContext.d.ts type PoolCacheEntry = { poolName: string; procedureNameCache: Cache<string>; argumentCache: Cache<argsType>; }; /** * Admin Context Class */ declare class AdminContext { readonly startTime: Date; readonly config: configType; readonly pools: Pool[]; readonly caches: PoolCacheEntry[]; readonly statsManager: StatsManager; private _paused; constructor(config: configType); /** * Register a PL/SQL handler with the admin context. * @param route - The route for the handler. * @param pool - The connection pool. * @param procedureNameCache - The procedure name cache. * @param argumentCache - The argument cache. */ registerHandler(route: string, pool: Pool, procedureNameCache: Cache<string>, argumentCache: Cache<argsType>): void; get paused(): boolean; setPaused(value: boolean): void; } //#endregion //#region src/backend/handler/plsql/handlerPlSql.d.ts type WebPlSqlRequestHandler = RequestHandler & { procedureNameCache: Cache<string>; argumentCache: Cache<argsType>; }; /** * Express middleware. * * @param connectionPool - The connection pool. * @param config - The configuration options. * @param adminContext - Optional admin context for self-registration and stats tracking. * @returns The handler. */ declare const handlerWebPlSql: (connectionPool: Pool, config: configPlSqlType, adminContext?: AdminContext) => WebPlSqlRequestHandler & { procedureNameCache: Cache<string>; argumentCache: Cache<argsType>; }; //#endregion //#region src/backend/handler/handlerLogger.d.ts /** * Create the upload middleware. * @param filename - Output filename. * @returns Request handler. */ declare const handlerLogger: (filename: string) => RequestHandler; //#endregion //#region src/backend/handler/handlerUpload.d.ts /** * Create the upload middleware. * @param uploadFileSizeLimit - Maximum size of each uploaded file in bytes or no limit if omitted. * @returns Request handler. */ declare const handlerUpload: (uploadFileSizeLimit?: number) => RequestHandler; //#endregion //#region src/backend/handler/handlerAdmin.d.ts /** * Create admin API router * @param adminContext - The admin context * @returns Express router */ declare const createAdminRouter: (adminContext: AdminContext) => Router; //#endregion //#region src/backend/handler/handlerAdminConsole.d.ts type AdminConsoleConfig = { /** Base route for the admin console (defaults to '/admin') */adminRoute?: string | undefined; /** Path to built admin frontend directory (optional - auto-detects if omitted) */ staticDir?: string | undefined; /** Optional username for basic auth */ user?: string | undefined; /** Optional password for basic auth */ password?: string | undefined; /** Skip static dir validation (for dev mode) */ devMode?: boolean | undefined; }; /** * Creates the admin console middleware. * @param config - The admin console configuration. * @param adminContext - The admin context. * @returns The express request handler. */ declare const handlerAdminConsole: (config: AdminConsoleConfig, adminContext: AdminContext) => RequestHandler; //#endregion //#region src/backend/handler/handlerSpaFallback.d.ts /** * Creates middleware that serves index.html for SPA routes. * * Handles React Router (createBrowserRouter), Vue Router, Angular Router, etc. * * **CRITICAL**: This middleware MUST be mounted AFTER express-static-gzip. * * Middleware order: * 1. express-static-gzip → Serves actual files (CSS, JS, images) * 2. handlerSpaFallback → Serves index.html for navigation requests * * @param directoryPath - Path to the static directory containing index.html * @param _route - The route prefix (for debugging) * @returns Express request handler * * @example * ```typescript * app.use('/app', expressStaticGzip('./build')); * app.use('/app', createSpaFallback('./build', '/app')); * ``` */ declare const createSpaFallback: (directoryPath: string, _route: string) => RequestHandler; //#endregion //#region src/backend/util/file.d.ts /** * Read file. * * @param filePath - File name. * @returns The string. */ declare const readFileSyncUtf8: (filePath: string) => string; /** * Read file. * * @param filePath - File name. * @returns The buffer. */ declare const readFile: (filePath: string) => Promise<Buffer>; /** * Remove file. * * @param filePath - File name. */ declare const removeFile: (filePath: string) => Promise<void>; /** * Load a json file. * * @param filePath - File name. * @returns The json object. */ declare const getJsonFile: (filePath: string) => unknown; /** * Is this a directory. * @param directoryPath - Directory name. * @returns Return true if it is a directory. */ declare const isDirectory: (directoryPath: unknown) => Promise<boolean>; /** * Is this a file. * @param filePath - File name. * @returns Return true if it is a file. */ declare const isFile: (filePath: unknown) => Promise<boolean>; //#endregion export { type AdminConsoleConfig, AdminContext, type configPlSqlType, type configType, createAdminRouter, createServer, createSpaFallback, forceShutdown, getJsonFile, getVersion, handlerAdminConsole, handlerLogger, handlerUpload, handlerWebPlSql, installShutdown, isDirectory, isFile, loadConfig, oracledb_provider_d_exports as oracledb, readFile, readFileSyncUtf8, removeFile, showConfig, startServer, startServerConfig, z$configType }; //# sourceMappingURL=index.d.mts.map