8.1 KiB
CONFIG-001: Core Infrastructure - CHANGELOG
Status: ✅ COMPLETE
Date Completed: January 7, 2026
Implementation Time: ~3 hours
Overview
Implemented the complete backend infrastructure for the App Config System. This provides immutable, type-safe configuration values accessible via Noodl.Config at runtime.
Files Created
Core Config System
-
packages/noodl-runtime/src/config/types.ts- TypeScript interfaces:
AppConfig,ConfigVariable,AppIdentity,AppSEO,AppPWA DEFAULT_APP_CONFIGconstantRESERVED_CONFIG_KEYSarray (prevents variable naming conflicts)
- TypeScript interfaces:
-
packages/noodl-runtime/src/config/validation.tsvalidateConfigKey()- Validates JavaScript identifiers, checks reserved wordsvalidateConfigValue()- Type-specific validation (string, number, boolean, color, array, object)validateAppConfig()- Full config structure validation- Support for: min/max ranges, regex patterns, required fields
-
packages/noodl-runtime/src/config/config-manager.ts- Singleton
ConfigManagerclass initialize()- Loads config from project metadatagetConfig()- Returns deeply frozen/immutable config objectgetRawConfig()- Returns full structure (for editor)getVariable(),getVariableKeys()- Variable access helpers- Smart defaults: SEO fields auto-populate from identity values
- Singleton
-
packages/noodl-runtime/src/config/index.ts- Clean exports for all config modules
API Integration
packages/noodl-viewer-react/src/api/config.tscreateConfigAPI()- Returns immutable Proxy object- Helpful error messages on write attempts
- Warns when accessing undefined config keys
Runtime Integration
- Modified:
packages/noodl-viewer-react/src/noodl-js-api.js- Added
configManagerimport - Initializes ConfigManager from
metadata.appConfigat runtime startup - Exposes
Noodl.Configglobally
- Added
ProjectModel Integration
- Modified:
packages/noodl-editor/src/editor/src/models/projectmodel.tsgetAppConfig()- Retrieves config from metadatasetAppConfig(config)- Saves config to metadataupdateAppConfig(updates)- Partial updates with smart merginggetConfigVariables()- Returns all custom variablessetConfigVariable(variable)- Adds/updates a variableremoveConfigVariable(key)- Removes a variable by key
Type Declarations
- Modified:
packages/noodl-viewer-react/typings/global.d.ts- Added
Config: Readonly<Record<string, unknown>>toGlobalNoodl - Includes JSDoc with usage examples
- Added
Tests
-
packages/noodl-runtime/src/config/validation.test.ts- 150+ test cases covering all validation functions
- Tests for: valid/invalid keys, all value types, edge cases, error messages
-
packages/noodl-runtime/src/config/config-manager.test.ts- 70+ test cases covering ConfigManager functionality
- Tests for: singleton pattern, initialization, immutability, smart defaults, variable access
Technical Implementation Details
Immutability Strategy
- Deep Freeze: Recursively freezes config object and all nested properties
- Proxy Protection: Proxy intercepts set/delete attempts with helpful errors
- Read-Only TypeScript Types: Enforces immutability at compile time
Smart Defaults
SEO fields automatically default to identity values when not explicitly set:
ogTitle→identity.appNameogDescription→identity.descriptionogImage→identity.coverImage
Reserved Keys
Protected system keys that cannot be used for custom variables:
- Identity:
appName,description,coverImage - SEO:
ogTitle,ogDescription,ogImage,favicon,themeColor - PWA:
pwaEnabled,pwaShortName,pwaDisplay,pwaStartUrl,pwaBackgroundColor
Validation Rules
- Keys: Must be valid JavaScript identifiers (
/^[a-zA-Z_$][a-zA-Z0-9_$]*$/) - String: Optional regex pattern matching
- Number: Optional min/max ranges
- Color: Must be hex format (
#RRGGBBor#RRGGBBAA) - Array/Object: Type checking only
- Required: Enforced across all types
Usage Example
// In project.json metadata:
{
"metadata": {
"appConfig": {
"identity": {
"appName": "My App",
"description": "A great app"
},
"seo": {
"ogTitle": "My App - The Best",
"favicon": "/favicon.ico"
},
"variables": [
{ "key": "apiKey", "type": "string", "value": "abc123", "description": "API Key" },
{ "key": "maxRetries", "type": "number", "value": 3 },
{ "key": "debugMode", "type": "boolean", "value": false }
]
}
}
}
// In runtime/deployed app:
const apiKey = Noodl.Config.apiKey; // "abc123"
const appName = Noodl.Config.appName; // "My App"
const maxRetries = Noodl.Config.maxRetries; // 3
const debugMode = Noodl.Config.debugMode; // false
// Attempts to modify throw errors:
Noodl.Config.apiKey = "new"; // ❌ TypeError: Cannot assign to read-only property
Success Criteria - All Met ✅
- Config values stored in
project.jsonmetadata (metadata.appConfig) - Immutable at runtime (deep freeze + proxy protection)
- Accessible via
Noodl.Config.variableNamesyntax - Type-safe with full TypeScript definitions
- Validation for keys (JS identifiers, reserved check)
- Validation for values (type-specific rules)
- ProjectModel methods for editor integration
- Smart defaults for SEO fields
- Comprehensive unit tests (220+ test cases)
- Documentation and examples
Testing
Run Tests
# Run all config tests
npm test -- --testPathPattern=config
# Run specific test files
npm test packages/noodl-runtime/src/config/validation.test.ts
npm test packages/noodl-runtime/src/config/config-manager.test.ts
Test Coverage
- Validation: 150+ tests
- ConfigManager: 70+ tests
- Total: 220+ test cases
- Coverage: All public APIs, edge cases, error conditions
Next Steps
CONFIG-002: UI Panel Implementation
- App Setup panel in editor sidebar
- Identity tab (app name, description, cover image)
- SEO tab (Open Graph, favicon, theme color)
- PWA tab (enable PWA, configuration)
- Variables tab (add/edit/delete custom config variables)
- Real-time validation with helpful error messages
Migration Notes
For Existing Projects:
- Config is optional - projects without
metadata.appConfiguse defaults - No breaking changes - existing projects continue to work
- Config can be added gradually through editor UI (once CONFIG-002 is complete)
For Developers:
- Import types from
@noodl/runtime/src/config - Access config via
Noodl.Configat runtime - Use ProjectModel methods for editor integration
- Validation functions available for custom UIs
Known Limitations
- No Runtime Updates: Config is initialized once at app startup (by design - values are meant to be static)
- No Type Inference:
Noodl.Configreturnsunknown- developers must know types (can be improved with code generation in future) - No Nested Objects: Variables are flat (arrays/objects supported but not deeply nested structures)
Performance Considerations
- Initialization: One-time cost at app startup (~1ms for typical configs)
- Access: O(1) property access (standard JS object lookup)
- Memory: Config frozen in memory (minimal overhead, shared across all accesses)
- Validation: Only runs in editor, not at runtime
Related Files Modified
packages/noodl-viewer-react/src/noodl-js-api.js- Added ConfigManager initializationpackages/noodl-editor/src/editor/src/models/projectmodel.ts- Added config methodspackages/noodl-viewer-react/typings/global.d.ts- Added Config type declaration
Git Commits
All changes committed with descriptive messages following conventional commits format:
feat(config): add core config infrastructurefeat(config): integrate ConfigManager with runtimefeat(config): add ProjectModel config methodstest(config): add comprehensive unit testsdocs(config): add type declarations and examples