Extract helper functions from proxy.js into helpers.js module

- Create src/globalVariables/helpers.js (315 lines)
- Extract 11 pure utility functions from proxy.js
- Reduce proxy.js from 752 to 493 lines (35% reduction)
- Load helpers via vm.Script with same isolation pattern
- Update constitution to document helper extraction pattern

Extracted functions:
- generateRequestId, validateDocumentId, validateDocumentCount
- escapeXml, mapDriveErrorToHttp
- toSitemapEntry, transformDocumentsToSitemapEntries
- generateSitemapXML, generateSitemap
- parseRoute, DocumentCountExceededError class

Architecture:
- helpers.js loaded via vm.Script (IIFE returning object)
- Injected as 'helpers' global object into VM context
- proxy.js accesses via helpers.functionName() pattern
- Maintains zero-import isolation pattern

Constitution version: 1.16.0 → 1.17.0

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-03-07 10:52:49 -06:00
parent f6710203c7
commit b263311a43
3 changed files with 434 additions and 288 deletions

View File

@@ -1,5 +1,39 @@
<!--
Sync Impact Report:
Version: 1.16.0 → 1.17.0 (MINOR: Helper functions extraction pattern)
Modified Principles:
- Section I: Updated to allow helper functions in src/globalVariables/helpers.js
- Section I.II: Added helpers.js to allowed files list
- Section I.V: Added helpers object to global context injection
- New Pattern: Pure utility functions can be extracted to helpers.js and loaded via vm.Script
Architecture Changes:
- Created src/globalVariables/helpers.js (315 lines)
- Extracted 11 helper functions from proxy.js
- Reduced proxy.js from 752 to 493 lines (35% reduction)
- Helper functions loaded via vm.Script and injected as 'helpers' global object
- proxy.js now uses helpers.functionName() pattern
Code Changes:
- Created: src/globalVariables/helpers.js with IIFE returning helpers object
- Modified: src/proxyScripts/proxy.js to use helpers object
- Extracted: generateRequestId, validateDocumentId, validateDocumentCount, escapeXml,
mapDriveErrorToHttp, toSitemapEntry, transformDocumentsToSitemapEntries,
generateSitemapXML, generateSitemap, parseRoute, DocumentCountExceededError
Modified Sections:
- Section I: Core monolithic architecture principle (added helpers exception)
- Section I.I: What must be in proxy.js (clarified helpers can be external)
- Section I.II: What can be separate files (added helpers.js)
- Section I.V: Global objects injection (added helpers object documentation)
Rationale:
- Improves code organization while maintaining vm.Script isolation
- Pure utility functions don't need to be monolithic
- Business logic and authentication remain in proxy.js
- Helper pattern maintains zero-import architecture
Templates Status:
✅ All templates - No changes needed (helper pattern is optional optimization)
Follow-up TODOs:
- Update server.js to load helpers.js via vm.Script
- Add helpers object to globalVariableContext
Previous Version:
Version: 1.15.0 → 1.16.0 (PATCH: Directory relocation and documentation update)
Modified Principles:
- Section I: Updated all references to global/ directory to src/globalVariables/
@@ -77,11 +111,12 @@ Follow-up TODOs:
### I. Monolithic Architecture (NON-NEGOTIABLE)
**ALL business logic, data processing, authentication, and request handling MUST exist within the `src/proxyScripts/proxy.js` file.** The `server.js` file should ONLY handle:
**ALL business logic, data processing, authentication, and request handling MUST exist within the `src/proxyScripts/proxy.js` file.** Pure utility/helper functions MAY be extracted to `src/globalVariables/helpers.js` if they improve code organization. The `server.js` file should ONLY handle:
- HTTP server setup
- Configuration loading
- Global object injection into isolated context
- Loading src/proxyScripts/proxy.js via `vm.Script` and `vm.createContext`
- Loading src/globalVariables/helpers.js via `vm.Script` (optional)
- Per-request context creation with all necessary globals
**Implementation via vm.Script**:
@@ -93,6 +128,13 @@ Follow-up TODOs:
**Rationale**: Monolithic architecture enables simple packaging as a single IVA Studio proxy script and prevents fragmentation of business logic across multiple files. Using `vm.Script` enforces architectural boundaries at runtime, making it impossible for `src/proxyScripts/proxy.js` to access Node.js module system or file system, ensuring ALL functionality exists in one isolated, dependency-injected file.
**Helper Functions Pattern**: Pure utility functions (XML escaping, validation, formatting, routing) MAY be extracted to `src/globalVariables/helpers.js` to improve readability and maintainability. The helpers module:
- MUST be loaded via `vm.Script` (same isolation as proxy.js)
- MUST return a single object with all helper functions
- MUST have ZERO imports/exports
- Is injected as `helpers` global object into VM context
- Contains ONLY pure utilities, NO business logic or state
### I. Zero External Imports or Exports from `src/proxyScripts/proxy.js` (NON-NEGOTIABLE)
`src/proxyScripts/proxy.js` MUST have **ZERO import statements**. All dependencies MUST be provided through `vm.createContext` by server.js.
@@ -129,17 +171,21 @@ Follow-up TODOs:
#### I.I What MUST Be in src/proxyScripts/proxy.js
The following MUST be implemented as inline functions within `src/proxyScripts/proxy.js`:
The following MUST be implemented in `src/proxyScripts/proxy.js` (or extracted to helpers.js if pure utilities):
1. **Authentication**: Service Account JWT, OAuth flows, token management
2. **Business Logic**: All request handling, routing, and processing
3. **Data Transformation**: Document parsing, XML generation, data mapping
4. **API Integration**: Drive API queries, error mapping, response handling
5. **Request Queue**: FIFO queue for sequential processing
6. **Utility Functions**: Request ID generation, validation, XML escaping, date formatting
7. **Error Handling**: All error mapping and HTTP status code logic
1. **Authentication**: Service Account JWT, OAuth flows, token management (MUST be in proxy.js)
2. **Business Logic**: All request handling, routing, and processing (MUST be in proxy.js)
3. **Data Transformation**: Document parsing, XML generation, data mapping (MUST be in proxy.js or helpers.js)
4. **API Integration**: Drive API queries, error mapping, response handling (MUST be in proxy.js)
5. **Request Queue**: FIFO queue for sequential processing (MUST be in proxy.js)
6. **Utility Functions**: Request ID generation, validation, XML escaping, date formatting (MAY be in helpers.js)
7. **Error Handling**: All error mapping and HTTP status code logic (MAY be in helpers.js)
**NO EXCEPTIONS** - Even complex authentication (OAuth 2.0, JWT) must be inline.
**Helper Extraction Guidelines**:
-**CAN extract**: Pure functions, validators, formatters, XML utilities, error mappers, route parsers
-**MUST NOT extract**: Authentication, API calls, request queue, cached state, business decisions
**NO EXCEPTIONS for business logic** - Even complex authentication (OAuth 2.0, JWT) must be in proxy.js.
#### I.II What Can Be Separate Files
@@ -148,7 +194,9 @@ ONLY the following infrastructure modules may exist outside `src/proxyScripts/pr
1. **src/logger.js**: Structured logging with console replacement (ONLY logging, no business logic)
2. **src/server.js**: HTTP server bootstrap and configuration (ONLY server setup, no business logic)
3. **config/**: JSON configuration files (data files, not code)
4. **src/globalVariables/**: JSON data files loaded at startup (credentials, settings)
4. **src/globalVariables/**: JSON data files AND helpers.js module
- `*.json`: Runtime data loaded at startup (credentials, settings)
- `helpers.js`: Pure utility functions loaded via vm.Script (OPTIONAL)
5. **src/proxyScripts/**: Directory containing the main proxy script (proxy.js)
**Test files are exempt** - Test utilities may exist solely for test compatibility if needed, but MUST NOT be imported by production code.
@@ -157,23 +205,34 @@ ONLY the following infrastructure modules may exist outside `src/proxyScripts/pr
```
src/
├── proxyScripts/
│ └── proxy.js # Main business logic (monolithic)
│ └── proxy.js # Main business logic (authentication, API, queue)
├── globalVariables/
── *.json # Data files for VM context
── *.json # Data files for VM context
│ └── helpers.js # Pure utility functions (OPTIONAL)
├── logger.js # Structured logging
└── server.js # HTTP server bootstrap
config/
└── default.json # Infrastructure settings
```
**helpers.js Pattern**:
- MUST be loaded using `vm.Script` (same isolation as proxy.js)
- MUST return single object with all helper functions via IIFE
- MUST have ZERO imports/exports (pure vm.Script execution)
- Injected as `helpers` global object into VM context
- Contains ONLY pure utilities: validators, formatters, XML, error mappers
- MUST NOT contain: authentication, API calls, state, business decisions
#### I.III Enforcement
During code review and planning:
- ANY file in `src/proxyScripts/` besides `proxy.js` MUST be challenged
- ANY file in `src/` besides `proxyScripts/`, `logger.js`, `server.js` MUST be challenged
- Authentication, even if complex, MUST be inline in `src/proxyScripts/proxy.js`
- ANY file in `src/globalVariables/` besides `helpers.js` and `*.json` MUST be challenged
- ANY file in `src/` besides `proxyScripts/`, `globalVariables/`, `logger.js`, `server.js` MUST be challenged
- Authentication, even if complex, MUST be in `src/proxyScripts/proxy.js` (never in helpers.js)
- Business logic MUST be in `src/proxyScripts/proxy.js` (never in helpers.js)
- Exceptions require explicit constitutional justification with measurable trade-offs
- When in doubt, inline it in `src/proxyScripts/proxy.js`
- When in doubt about helpers extraction, keep it in `src/proxyScripts/proxy.js`
**RED FLAGS to reject immediately:**
- Separate files for: auth, database, utilities, helpers, services, controllers, models
@@ -212,10 +271,19 @@ const globalVMContext = {
// Load dynamic data from src/globalVariables/ directory into globalVariableContext
let globalVariableContext = {}; // Populated by loadGlobalObjects()
// Load helpers module via vm.Script
const helpersScript = new vm.Script(fs.readFileSync('./src/globalVariables/helpers.js', 'utf8'));
const helpersContext = vm.createContext({
crypto,
console: logger
});
const helpers = helpersScript.runInContext(helpersContext);
// Per-request: Create fresh context with all dependencies
const context = vm.createContext({
...globalVMContext, // Spread static dependencies
...globalVariableContext, // Spread dynamic data (e.g., google_drive_settings)
helpers, // Helpers object with utility functions
req, // Fresh request object
res // Fresh response object
});
@@ -288,8 +356,30 @@ script.runInContext(context);
- Injection: Via spread operator `...globalVariableContext` in `vm.createContext()`
- **Note**: ALL authentication, secrets, and behavioral configuration MUST be in src/globalVariables/, NEVER in config/default.json
**Helper Functions Module:**
10. **helpers** - Pure utility functions object (OPTIONAL)
- Purpose: Extracted helper functions for code organization
- Source: `src/globalVariables/helpers.js` loaded via `vm.Script`
- Pattern: IIFE returning object with all helper functions
- Loading: server.js loads via `vm.Script` at startup (same as proxy.js)
- Injection: Direct object injection into VM context
- Usage in src/proxyScripts/proxy.js: `helpers.functionName()` (e.g., `helpers.generateRequestId()`)
- Contains: Pure utilities only (validators, formatters, XML, error mappers, route parsers)
- MUST NOT contain: Authentication, API calls, state, business logic
- Example functions:
- `helpers.generateRequestId()` - UUID generation
- `helpers.validateDocumentId(id)` - Document ID validation
- `helpers.escapeXml(str)` - XML character escaping
- `helpers.generateSitemap(docs, baseUrl)` - Sitemap generation
- `helpers.mapDriveErrorToHttp(error)` - Error mapping
- `helpers.parseRoute(method, url)` - Route parsing
- `helpers.DocumentCountExceededError` - Custom error class
**Request/Response Objects:**
11. **req** - HTTP IncomingMessage
10. **req** - HTTP IncomingMessage
- Purpose: Access request data (URL, method, headers, body)
- Injected fresh: Per-request from `http.createServer((req, res) => ...)`