- Refactor kmeContentSourceAdapter.js into getValidToken(), oidcAuthFlow(), and sitemapFlow(); add sitemap generation using hydra:member response structure - Add searchApiBaseUrl, tenant, proxyBaseUrl fields to kme_CSA_settings.json and kme_CSA_settings.json.example - Add 17 unit tests for sitemap flow and non-sitemap routing regression - Add 5 contract tests for sitemap endpoint (proxy-http.test.js) - Add [Unreleased] sitemap entry to CHANGELOG.md - Add full specs/002-sitemap-generation/ artifact directory (spec, plan, tasks, data-model, contracts, research, quickstart, checklist) - Update constitution.md: add redis as permitted global, refresh kme_CSA_settings references - Update copilot-instructions.md SPECKIT marker to sitemap plan
111 lines
4.8 KiB
Markdown
111 lines
4.8 KiB
Markdown
<!-- SPECKIT START -->
|
|
For additional context about technologies to be used, project structure,
|
|
shell commands, and other important information, read the current plan at
|
|
`specs/002-sitemap-generation/plan.md`
|
|
<!-- SPECKIT END -->
|
|
|
|
## Project Overview
|
|
|
|
`kme-content-adapter` is an HTTP proxy adapter (Node.js ≥18, ESM) that searches and exports documents from KME via a single proxy script executed in a VM sandbox.
|
|
|
|
## Commands
|
|
|
|
```bash
|
|
npm run dev # Start with --watch (auto-restart on file changes)
|
|
npm start # Start, piping logs through jq for pretty-printing
|
|
npm test # Run all tests
|
|
npm run test:unit # Unit tests only
|
|
npm run test:integration # Integration tests only
|
|
npm run test:contract # Contract tests only
|
|
|
|
# Run a single test file
|
|
node --test tests/unit/my.test.js
|
|
```
|
|
|
|
Tests use the Node.js built-in test runner (`node:test`). No test framework is installed.
|
|
|
|
Environment overrides: `PORT`, `HOST`, `LOG_LEVEL`.
|
|
|
|
## Architecture
|
|
|
|
The server loads `src/proxyScripts/proxy.js` once at startup using `vm.Script`, then executes it in a **fresh isolated VM context per request** via `vm.createContext`. This mirrors the target deployment environment (IVA Studio proxy script).
|
|
|
|
```
|
|
src/
|
|
├── proxyScripts/
|
|
│ └── proxy.js # ALL business logic lives here
|
|
├── globalVariables/
|
|
│ ├── *.json # Runtime data injected into VM context
|
|
│ └── adapterHelper.js # Pure utility functions (optional)
|
|
├── logger.js # Structured JSON logger
|
|
└── server.js # HTTP server bootstrap only
|
|
config/
|
|
└── default.json # Infrastructure settings (port, host, log level)
|
|
```
|
|
|
|
**Context injection** — `server.js` injects these globals into every request context:
|
|
|
|
| Variable | Source |
|
|
|---|---|
|
|
| `console` | Custom structured logger (`logger.js`) |
|
|
| `crypto` | Node.js Web Crypto API |
|
|
| `axios` | HTTP client |
|
|
| `jwt` | `jsonwebtoken` |
|
|
| `uuidv4` | UUID v4 generator |
|
|
| `xmlBuilder` | `xmlbuilder2` `create` function |
|
|
| `URLSearchParams`, `URL` | Node.js globals |
|
|
| `adapterHelper` | Loaded from `src/globalVariables/adapterHelper.js` (if present) |
|
|
| `<name>` | Each JSON/JS file in `src/globalVariables/` (filename → variable name) |
|
|
| `req`, `res` | Node.js HTTP request/response |
|
|
|
|
Routing metadata (`workspaceId`, `branch`, `route`) is attached by `server.js` to `req.params` before invoking the proxy — `proxy.js` must read these from `req.params`, never from `config`.
|
|
|
|
## Key Conventions
|
|
|
|
### `src/proxyScripts/proxy.js` — The Only Place for Business Logic
|
|
|
|
- **ZERO `import`/`export` statements** — the file runs in a VM with no module system access.
|
|
- **ZERO access to `config`, `global.config`, or `process.env`** — these are server concerns.
|
|
- All dependencies arrive via the injected VM context (see table above).
|
|
- Access injected variables directly: `adapter_settings.key`, not `globalThis["adapter_settings"]`.
|
|
|
|
### `src/globalVariables/adapterHelper.js` — Literal Function Body Pattern
|
|
|
|
This file contains the **literal body of a function**, not valid standalone JavaScript. `server.js` wraps it at load time:
|
|
|
|
```javascript
|
|
// server.js wraps the file contents like this:
|
|
const wrappedCode = `(function() {\n${code}\n})()`;
|
|
```
|
|
|
|
So `adapterHelper.js` must end with a bare `return { ... }` that exports the helpers object. It must have zero imports/exports and contain only pure utilities (validators, formatters, XML helpers, error mappers). Authentication, API calls, and state must stay in `proxy.js`.
|
|
|
|
### `src/globalVariables/` — Filename Is the Variable Name
|
|
|
|
Every file loaded from this directory is injected into the VM context using its filename (without extension) as the key:
|
|
|
|
- `adapter_settings.json` → available as `adapter_settings` in `proxy.js`
|
|
- `adapterHelper.js` → available as `adapterHelper` in `proxy.js`
|
|
- Files matching `*.example.*` are skipped.
|
|
- JSON files are loaded before JS files (so JS modules can reference JSON data).
|
|
|
|
### Config vs. Secrets
|
|
|
|
- `config/default.json` — infrastructure only: `server.port`, `server.host`, `logging.level`, `proxy.*`
|
|
- Credentials, API keys, and behavioral config → JSON files in `src/globalVariables/`
|
|
|
|
### Logging
|
|
|
|
Use the `logger` from `src/logger.js` in server-side code. It accepts either a string or a structured object:
|
|
|
|
```javascript
|
|
logger.info("Simple message");
|
|
logger.info({ message: "Structured", requestId: "abc", status: 200 });
|
|
```
|
|
|
|
In `proxy.js`, use the injected `console` object (same API).
|
|
|
|
### Challenging New Files
|
|
|
|
Before adding any file to `src/`, verify it belongs to one of the five allowed categories (server.js, logger.js, proxyScripts/proxy.js, globalVariables/*.json, globalVariables/adapterHelper.js). New files require explicit justification against the monolithic architecture constraint.
|