- Add contentFetchFlow() to proxy (FR-001 through FR-012) - Add extractArticleBody() helper with vkm:articleBody / articleBody fallback - Dynamic proxyBaseUrl derivation from x-forwarded-proto/host headers - Forward query/size/category params on /sitemap.xml requests - Add Accept: application/ld+json header to content API calls - Remove oidcAuthFlow() - unmatched requests now return 404 Not Found - Fix xmlbuilder2 import: default import, call as xmlbuilder2.create(...) - Version bump 0.2.0 → 0.3.0 - 45/45 tests passing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
4.8 KiB
For additional context about technologies to be used, project structure,
shell commands, and other important information, read the current plan at
specs/003-kme-content-fetch/plan.md
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
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 |
xmlbuilder2 |
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/exportstatements — the file runs in a VM with no module system access. - ZERO access to
config,global.config, orprocess.env— these are server concerns. - All dependencies arrive via the injected VM context (see table above).
- Access injected variables directly:
adapter_settings.key, notglobalThis["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:
// 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 asadapter_settingsinproxy.jsadapterHelper.js→ available asadapterHelperinproxy.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:
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.