This document provides guidelines for AI agents working on the web_plsql repository.
Adhere strictly to these rules to ensure code quality, consistency, and stability.
This project is a Node.js application using TypeScript and ES Modules (ESM).
- Install Dependencies:
npm install - Run All Tests:
npm run test(ornpm test)- Uses Vitest runner (
vitest run). - Coverage: Run
npm run test -- --coverageto generate a coverage report.
- Uses Vitest runner (
- Run Single Test:
npx vitest run tests/backend/version.test.ts- Example:
npx vitest run tests/backend/version.test.ts - Note: The project uses Vitest.
- Example:
- Lint & Format:
npm run lint- Runs
prettier(formatting),eslint(linting), andtsc(type checking). - Crucial: Always run this before submitting changes.
- Runs
- Type Check:
tsc --noEmit(included innpm run lint). - Build Backend:
npm run build:backend- Uses tsdown to bundle the backend into
dist/.
- Uses tsdown to bundle the backend into
- Build Admin:
npm run build:frontend- Uses Vite to bundle the admin console.
- Clean:
npm run clean- Removes build artifacts and logs.
- Full CI Check:
npm run ci- Runs clean, version check, build, lint, and coverage.
- Build Image:
npm run image-build - Save Image:
npm run image-save
- Language: TypeScript.
- Type Checking: Strict TypeScript checking.
- Module System: ES Modules (
type: "module").- Requirement: All imports must include the
.tsor.jsfile extension as required by the environment. - Example:
import { foo } from './bar.ts'.
- Requirement: All imports must include the
- Always Use Latest Versions: When adding or updating dependencies, always use the latest stable versions available via
npm install package-name@latest. - No Type Suppression: Never use
@ts-ignore,// @ts-nocheck, oranytype.- If a TypeScript/ESLint error cannot be resolved, fix the code instead of suppressing the error.
- Exception: Only if absolutely necessary, use
// @ts-expect-errorwith a detailed explanation comment explaining why it was absolutely required.
- No ESLint Disabling: Never disable ESLint rules with
/* eslint-disable */or inline disable comments.- Exception: Only if absolutely necessary, disable a specific rule with
// eslint-disable-next-line rule-namefollowed by a detailed explanation comment explaining why it was absolutely required.
- Exception: Only if absolutely necessary, disable a specific rule with
- No Type Assertions: Never use TypeScript
astype assertions (e.g.,value as string).- Use proper type narrowing, type guards, or explicit checks (e.g.,
typeof value === 'string'). - Throw exceptions for unexpected types rather than silently converting.
- If you must cast, explain why in comments and prefer runtime checks over compile-time assertions.
- Use proper type narrowing, type guards, or explicit checks (e.g.,
- Formatter: Prettier.
- Linter: ESLint with
typescript-eslintstrict rules. - Specific Rules:
- Unused Variables: Warn (
argsIgnorePattern: '^_'allowed). - Strict Types: No implicit any, strict null checks.
- JSDoc: Required for public functions/methods.
- Error Handling:
caughtErrorscheck is disabled, but errors must be handled explicitly. - Catch Clause Variables: Always type catch clause variables as
unknown(e.g.,catch (error: unknown)). Do not useanyor leave it untyped.
- Unused Variables: Warn (
- Variables/Functions:
camelCase(e.g.,handlerWebPlSql,invokeProcedure). - Classes:
PascalCase(e.g.,ProcedureError). - Files:
camelCase(e.g.,handlerPlSql.js). - Constants:
UPPER_SNAKE_CASEorcamelCasedepending on usage.
Group imports in the following order:
- Node.js built-ins (prefix with
node:, e.g.,import fs from 'node:fs';). - External libraries (e.g.,
import oracledb from 'oracledb';). - Internal modules (relative paths with
.js, e.g.,import { util } from '../../util/util.js';).
- Use standard
try...catchblocks. - For procedure execution, wrap errors in
ProcedureErrorto capture context (SQL, binds). - Do not swallow errors silently. Log them using the debug module or rethrow.
- Upload: Files are uploaded if present.
- Sanitize & Resolve: The procedure name is validated against the database using
dbms_utility.name_resolve. This prevents SQL injection and ensures the object exists. - Prepare: The session is prepared (see below).
- Execute: The PL/SQL procedure is executed.
- Fetch: The page content is retrieved via OWA.
- Download: File downloads are handled.
- Response: The parsed page is sent to the client.
To ensure performance and stability, the middleware uses a robust caching mechanism:
- Scoped Caching: Caches are instantiated per handler (pool), preventing multi-tenant collisions.
- LFU Policy: The
Cacheutility (src/backend/util/cache.ts) implements a Least-Frequently-Used eviction policy to prevent memory overflows. - Invalidation: Logic in
procedure.tsautomatically clears cache entries when database errors indicating schema changes (ORA-04068,ORA-06550, etc.) are detected.
The middleware enforces a stateless model as defined in the Oracle mod_plsql documentation (Section 3.4 Transaction Mode).
- Mechanism:
dbms_session.modify_package_state(dbms_session.reinitialize)is called before executing the user's procedure. - Purpose: To reset the state of all packages in the session.
- Necessity: Essential for correctness and security in a pooled connection environment. Without this, state leakage occurs (User B inherits User A's global variables), causing severe security and logic failures.
- The "Stateful" Exception: Legacy
mod_plsqlhad aStatelessWithPreservePackageStateoption. This mode skipped the reset but required manual cleanup by the developer. - Decision: This architecture strictly prohibits this mode.
- Reasoning: It is dangerous (security risk) and generally discouraged.
web_plsqlhardcodes the "Fast Reset" approach (dbms_session.reinitialize) to ensure a secure, scalable gateway. - Instruction: Do not remove this call. It is essential for correctness.
- Character Sets: The project currently hardcodes some charsets (
ascii,UTF8). Aim for configuration-driven NLS settings.
The admin console is a Single Page Application (SPA) built with modern web technologies and served via an internal API.
Architecture:
- Backend: Express routes under
/admin/apiserve JSON data for traffic, pools, logs, and cache. - Frontend: TypeScript-based SPA bundled with Vite.
- Styling: Tailwind CSS for a responsive and modern UI.
- Charts: Chart.js for real-time data visualization.
- Icons: Heroicons (SVG).
Key Components:
src/frontend/api.ts: Typed client for the internal Admin API.src/frontend/ui/views.ts: View-specific rendering and refresh logic.src/frontend/charts.ts: Chart.js lifecycle management and real-time updates.src/frontend/main.ts: Main application logic and state management.
Build Process:
npm run build:frontend: Compiles TS, processes Tailwind CSS, and bundles all assets intosrc/frontend/lib/chart.bundle.js.- The bundle includes Chart.js and all required logic, making the frontend self-contained.
- Note: Always run this command after modifying any file in
src/frontend/.
View Refresh Logic:
- Each view (errors, access, cache, pools, config, system) has a refresh function in
src/frontend/ui/views.ts - Views are refreshed when:
- The view is first clicked/opened
- Auto-refresh timer fires (if the view is currently active)
- The navigation handler in
src/frontend/main.tscalls the appropriate refresh function for each view
Commands:
npm run build:frontend- Build the admin client bundlenpm run build- Alias for build:frontendnpm run prepack- Automatically builds before publishingnpm run ci- Runs full CI including build
Files Excluded from Linting/Type Checking:
src/frontend/lib/- Build output directory (contains bundled JS/CSS)
The middleware supports serving SPA applications that use HTML5 History Mode routing (e.g., React Router createBrowserRouter, Vue Router history mode).
Configuration:
Enable SPA fallback in routeStatic configuration:
{
"routeStatic": [
{
"route": "/app",
"directoryPath": "./build",
"spaFallback": true
}
]
}How It Works:
- express-static-gzip serves actual files (CSS, JS, images)
- If no file found, handlerSpaFallback serves
index.html - SPA router (React Router, etc.) handles client-side routing
Middleware Order (CRITICAL):
// ✅ CORRECT ORDER
app.use('/app', expressStaticGzip('./build')); // Serves files
app.use('/app', createSpaFallback('./build', '/app')); // Fallback to index.html
// ❌ WRONG ORDER (will not work)
app.use('/app', createSpaFallback('./build', '/app')); // Always serves index.html
app.use('/app', expressStaticGzip('./build')); // Never reached!Accept Header Filtering: The fallback only serves HTML for navigation requests. API requests and static asset 404s are passed through:
Accept: text/html→ Serves index.htmlAccept: application/json→ Calls next() (404)Accept: image/png→ Calls next() (404)
Example Use Cases:
- React Router with
createBrowserRouter - Vue Router with
createWebHistory - Angular Router with
useHash: false - Any SPA framework using History API
Note: The admin console does NOT need SPA fallback (uses hash-based routing internally).
Refer to ENHANCEMENTS.md for the full roadmap. Key priorities include:
- Hooks: Adding
before/afterprocedure hooks for session setup. - Streaming: Moving away from in-memory buffering for large responses.
- Framework: Vitest.
- Unit Tests: Colocated with source files in
src/directory.- Each module should have one test file named
<module>.test.tsalongside<module>.ts. - Example:
src/backend/util/cache.test.tstestssrc/backend/util/cache.ts.
- Each module should have one test file named
- Integration Tests: Located in
tests/backend/integration/.- Tests that require full middleware stack or database mocking.
- File naming:
*.test.ts.
- E2E Tests: Located in
tests/e2e/.- Browser-based tests using Playwright.
- File naming:
*.e2e.test.ts.
- Performance Tests: Located in
tests/.- File naming:
performance.test.ts.
- File naming:
- Unit Tests:
<source-name>.test.ts(e.g.,cache.test.tstestscache.ts). - Integration Tests:
*.test.tsintests/backend/integration/. - E2E Tests:
*.e2e.test.tsintests/e2e/.
- Vitest Config:
vitest.config.jsincludes both patterns:include: ['tests/**/*.test.{js,ts}', 'src/**/*.test.{js,ts}']
- ESLint Config:
eslint.config.jsapplies relaxed test rules to both:files: ['tests/**/*.{js,ts}', 'src/**/*.test.ts']
- Unit tests should aim for 100% coverage for modules they test.
- Integration and E2E tests contribute to overall coverage but have separate thresholds.
console.warnandconsole.errorare suppressed by default during tests.- To see full logs, run:
DEBUG=true npm testorVERBOSE=true npm test. - See
tests/setup.tsfor implementation.
- To see full logs, run:
Unit Test Pattern:
import { describe, it, expect } from 'vitest';
import { myFunction } from './myModule.js';
describe('myModule', () => {
it('should return expected value', () => {
const result = myFunction();
expect(result).toBe('expected');
});
});Integration Test Pattern (in tests/backend/integration/):
import { describe, it, beforeAll, afterAll } from 'vitest';
import request from 'supertest';
import { serverStart, serverStop } from '../../src/backend/server/server.js';
describe('middleware', () => {
let server;
beforeAll(async () => { server = await serverStart({...}); });
afterAll(async () => { await serverStop(server); });
it('should handle request', async () => {
const res = await request(server.app).get('/');
expect(res.status).toBe(200);
});
});- TypeScript strict rules are relaxed for test files (see
eslint.config.js). - Allowed in tests:
no-explicit-any,no-unsafe-argument,dot-notation, etc.
- Understand: Read existing code. Search using
grepandglob. Checksrc/backend/types.tsfor data structures. - Check Enhancements: Consult
ENHANCEMENTS.mdto avoid reimplementing known issues or contradicting future plans. - Consult References: Check official docs and
thoth-gateway(see below) for architectural alignment. - Plan: Outline changes. Check for existing tests.
- Implement: Write code in
.tsfiles. - Verify:
- Run
npm run lintto fix formatting and type errors immediately. - Run
npx vitest run tests/relevant.test.tsto verify logic.
- Run
- Finalize: ALWAYS Run
npm run cito ensure the full suite passes. This is MANDATORY before finishing a task.
src/: Source code.backend/:handler/: Request handlers (handlerPlSql.ts).handler/plsql/: PL/SQL execution logic (procedure.ts,parsePage.ts).server/: Server setup.util/: Utilities.
frontend/: Admin console source.common/: Shared constants and logic.
tests/: Unit, integration, and e2e tests.types/: Generated TypeScript definitions (do not edit).examples/: Example configurations and SQL.
Always consult these official resources and reference implementations when planning architectural changes or verifying behavior. This project aims to be a viable alternative to these standards.
- Official Oracle Documentation:
- mod_plsql (Legacy): Oracle Application Server mod_plsql User's Guide 10g Release 2 (10.1.2)
- Crucial Sections: Section 3.4 (Transaction Mode), Section 3.6 (Parameter Passing), Section 3.9 (CGI Variables).
- ORDS (Current): Oracle REST Data Services Developer's Guide
- The modern successor; align behavior with ORDS standards where applicable.
- mod_plsql (Legacy): Oracle Application Server mod_plsql User's Guide 10g Release 2 (10.1.2)
- Thoth Gateway: https://github.com/mortenbra/thoth-gateway
- Use this as a comparative reference for features like hooks, caching, and SOAP support.
- This is an established open-source alternative for IIS that implements the same standard.