@ticatec/dyna-js
v0.2.1
Published
A modern TypeScript library for safe dynamic code execution in browser environments with intelligent module caching and configurable security policies
Readme
@ticatec/dyna-js
A modern TypeScript library for safe dynamic code execution in browser environments using new Function() with configurable security policies and intelligent module caching.
✨ Features
🚀 Modern Browser Library - Built for modern browsers with ESM modules only
🔒 Secure by Default - Configurable security policies to control code execution
⚡ Performance Optimized - Parallel module loading and intelligent caching
💾 Smart Storage - Unified localStorage management with version control
📦 TypeScript Support - Full type safety with comprehensive type definitions
🎯 Singleton Pattern - Initialize once, use anywhere
🔧 Developer Friendly - Simple API with powerful features
📦 Installation
npm install @ticatec/dyna-js🚀 Quick Start
1. Initialize DynaJs (Application Startup)
import { initializeDynaJs } from '@ticatec/dyna-js';
// Initialize with your classes and functions
initializeDynaJs({
defaultImports: {
FlexiForm: FlexiFormClass,
Dialog: DialogClass,
MessageBox: MessageBoxClass
},
allowBrowserAPIs: false, // Secure by default
validateCode: true
});2. Execute Dynamic Code
import { getDynaJs } from '@ticatec/dyna-js';
const loader = getDynaJs();
// Create dynamic form class
const MyFormClass = loader.executeSync(`
class CustomForm extends FlexiForm {
constructor() {
super();
this.dialog = Dialog;
}
show() {
MessageBox.info('Form is ready!');
}
render() {
return `
<div class="form">
<h1>Dynamic Form</h1>
<p>Created dynamically!</p>
</div>
`;
}
}
return CustomForm;
`);
// Use the dynamically created class
const form = new MyFormClass();
form.show();📚 API Reference
DynaJs
executeSync<T>(code: string, options?: ExecutionOptions): T
Synchronously execute code and return the result.
const result = loader.executeSync(`
return new FlexiForm();
`);execute<T>(code: string, options?: ExecutionOptions): Promise<T>
Asynchronously execute code with timeout support.
const result = await loader.execute(`
return await fetchData();
`, { timeout: 3000 });createFunction<T>(code: string, paramNames?: string[], options?: ExecutionOptions): T
Create a reusable function from dynamic code.
const validator = loader.createFunction(`
return function(data) {
return data.name && data.email;
};
`);
const isValid = validator({ name: 'John', email: '[email protected]' });Configuration Options
interface DynaJsConfig {
defaultTimeout?: number; // Default: 5000ms
defaultStrict?: boolean; // Default: true
allowedGlobals?: string[]; // Whitelist of allowed global variables
blockedGlobals?: string[]; // Blacklist of blocked variables
defaultImports?: ModuleImports; // Pre-imported classes/functions
allowTimers?: boolean; // Allow setTimeout/setInterval (Default: false)
allowDynamicImports?: boolean; // Allow import()/require() (Default: false)
validateCode?: boolean; // Enable code validation (Default: true)
allowBrowserAPIs?: boolean; // Allow window/document access (Default: false)
}🔐 Security Configurations
🔒 Strict Mode (Recommended, Default)
initializeDynaJs({
defaultImports: { FlexiForm, Dialog },
allowBrowserAPIs: false, // Block window, document, localStorage
allowTimers: false, // Block setTimeout/setInterval
validateCode: true // Enable code pattern validation
});
// ❌ These will be blocked:
// window.location.href = 'malicious-site.com'
// localStorage.clear()
// setTimeout(maliciousFunction, 1000)🟡 Permissive Mode
initializeDynaJs({
defaultImports: { FlexiForm, Dialog },
allowBrowserAPIs: true, // ✅ Allow browser APIs
allowTimers: true, // ✅ Allow timers
validateCode: false // Disable validation
});📦 ModuleLoader - Dynamic Module Management
ModuleLoader manages dynamic JavaScript modules with automatic caching, version control, and parallel loading.
Basic Usage
1. Initialize ModuleLoader
import { ModuleLoader } from '@ticatec/dyna-js';
const loadModule = async (moduleInfo) => {
const response = await fetch(`/api/modules/${moduleInfo.code}`);
return response.json(); // Should return { code, digest, scriptText, uiLayout? }
};
const moduleLoader = ModuleLoader.initialize(loadModule, {
keyField: 'code', // Default: 'code'
prefix: 'MyApp' // Default: 'DynaJS'
});2. Check and Update Modules (Parallel Loading)
// Check and update all stale modules in parallel
await moduleLoader.checkFreshScripts([
{ code: 'user-form', digest: 'abc123...' },
{ code: 'data-grid', digest: 'def456...' },
{ code: 'chart-widget', digest: 'ghi789...' }
]);
// Performance: If 3 modules need updating:
// Serial: 200ms + 300ms + 250ms = 750ms
// Parallel: max(200ms, 300ms, 250ms) = 300ms ✅3. Create and Use Modules
const UserForm = moduleLoader.createModule('user-form', {
React,
ReactDOM,
Dialog
});
const form = new UserForm({ title: 'User Registration' });ModuleLoader API Reference
Static Methods
ModuleLoader.initialize(loadModule, options?)
Initialize or get the ModuleLoader singleton instance.
const loader = ModuleLoader.initialize(
async (info) => {
const response = await fetch(`/api/modules/${info.code}`);
return response.json();
},
{
keyField: 'code', // Field name for module identifier
prefix: 'MyApp', // Storage prefix for localStorage
moduleCheck: customCheck // Optional custom freshness check
}
);Parameters:
loadModule(LoadModule): Function to fetch module data from remoteoptions(ModuleLoaderOptions): Optional configurationkeyField: Field name containing module identifier (default:'code')prefix: Storage prefix for localStorage keys (default:'DynaJS')moduleCheck: Custom function to check if module needs update
Returns: The singleton ModuleLoader instance
ModuleLoader.getInstance()
Get the existing ModuleLoader singleton instance. Throws if not initialized.
// In main file
ModuleLoader.initialize(loadFn, options);
// In another file
const loader = ModuleLoader.getInstance();Returns: The ModuleLoader instance Throws: Error if instance has not been initialized
ModuleLoader.reset()
Reset the singleton instance. Primarily useful for testing.
afterEach(() => {
ModuleLoader.reset();
});Instance Methods
checkFreshScripts(list)
Check and update modules that are not fresh. Performs parallel loading for optimal performance.
await loader.checkFreshScripts([
{ code: 'user-form', digest: 'abc123' },
{ code: 'data-grid', digest: 'def456' }
]);Parameters:
list(Array): Array of module data objects to check
Returns: Promise that resolves when all modules are checked and updated
Performance: Parallel loading is N× faster than serial for N modules
createModule<T>(key, imports)
Create or retrieve a cached module instance.
// Create a React component class
const UserForm = loader.createModule<typeof React.Component>('user-form', {
React,
ReactDOM,
Dialog
});
const form = new UserForm({ title: 'User Registration' });Parameters:
key(string): The module key/code to loadimports(any): Object containing dependencies to inject into module scope
Returns: The instantiated module or cached instance
Throws: Error if module not found in storage (call checkFreshScripts first)
getLayout<T>(key)
Get the UI layout configuration for a module.
interface FormLayout {
fields: Array<{ name: string; type: string }>;
structure: Record<string, any>;
}
const layout = loader.getLayout<FormLayout>('user-form');
if (layout) {
console.log('Form fields:', layout.fields);
console.log('Form structure:', layout.structure);
}Parameters:
key(string): Module identifier
Returns: Parsed layout object or null if not found
getDigest(key)
Get the current digest (version hash) for a module.
const digest = loader.getDigest('user-form');
console.log(`Current version: ${digest?.substring(0, 8)}`);Parameters:
key(string): Module identifier
Returns: Digest string or null if module not found
removeModule(key)
Remove a specific module from localStorage and memory cache.
loader.removeModule('deprecated-form');Parameters:
key(string): Module identifier
clearAll()
Clear all modules from localStorage and memory cache.
const count = loader.clearAll();
console.log(`Cleared ${count} modules`);Returns: Number of modules cleared
getAllModuleKeys()
Get all module keys currently stored in localStorage.
const keys = loader.getAllModuleKeys();
console.log('Available modules:', keys);
// ['user-form', 'data-grid', 'chart-widget', ...]Returns: Array of module keys
getStorageStats()
Get storage usage statistics for all modules.
const stats = loader.getStorageStats();
console.log(`Modules: ${stats.moduleCount}`);
console.log(`Total size: ${(stats.totalSize / 1024).toFixed(2)} KB`);
// Find old modules for cleanup
const oldModules = stats.modules.filter(
m => Date.now() - m.timestamp > 30 * 24 * 60 * 60 * 1000
);Returns: Object containing:
moduleCount(number): Total number of modulestotalSize(number): Total storage size in bytesmodules(Array): Per-module details with key, size, and timestamp
StorageManager - Unified Storage API
StorageManager provides a unified API for localStorage operations with built-in error handling and version control.
import { StorageManager } from '@ticatec/dyna-js';
// Get storage statistics
const stats = StorageManager.getStorageStats('MyApp');
console.log(`Modules: ${stats.moduleCount}, Size: ${stats.totalSize} bytes`);
// Get all module keys
const keys = StorageManager.getAllModuleKeys('MyApp');
// Clear all modules
const count = StorageManager.clearAll('MyApp');
// Check if module is fresh
const isFresh = StorageManager.isModuleFresh('MyApp', 'module-name', 'digest-value');Storage Data Structure
Modules are stored as a single JSON object:
{
metadata: {
digest: string; // Version hash
version: number; // Storage format version
timestamp: number; // Last update time
size: number; // Script size in bytes
},
scriptText: string; // The actual code
uiLayout?: string; // Optional UI layout (JSON string)
}🎯 Use Cases
Dynamic Form Components
const DynamicFormBuilder = loader.executeSync(`
class FormBuilder extends FlexiForm {
constructor(config) {
super();
this.config = config;
this.fields = [];
}
addField(fieldConfig) {
this.fields.push(fieldConfig);
return this;
}
build() {
return this.fields.map(field =>
new FlexiCard({ title: field.label })
);
}
}
return FormBuilder;
`);Validation Functions
const validator = loader.createFunction(`
return function validateEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
};
`);
validator('[email protected]'); // true🔧 Advanced Usage
Storage Management
// Monitor storage usage
const stats = moduleLoader.getStorageStats();
if (stats.totalSize > 5_000_000) { // 5MB
console.warn('Storage quota running low!');
// Clear old modules
const oldModules = stats.modules
.filter(m => Date.now() - m.timestamp > 30 * 24 * 60 * 60 * 1000)
.map(m => m.key);
oldModules.forEach(key => moduleLoader.removeModule(key));
}Custom Module Check
const moduleLoader = ModuleLoader.initialize(loadModule, {
moduleCheck: (moduleInfo) => {
// Custom freshness check logic
const localData = StorageManager.loadModule('MyApp', moduleInfo.code);
return localData?.metadata.digest === moduleInfo.digest;
}
});🌐 Browser Support
- Chrome 90+
- Firefox 88+
- Safari 14+
- Edge 90+
Requirement: Modern browsers with ES2020+ support
📝 TypeScript Support
Full TypeScript support with comprehensive type definitions:
import {
DynaJs,
ModuleLoader,
StorageManager,
StorageKey,
ExecutionOptions,
ModuleImports,
StoredModuleData,
ModuleMetadata
} from '@ticatec/dyna-js';🏗️ Building
npm run build # Build ESM modules
npm run build:watch # Watch mode
npm run typecheck # Type checking only
npm run clean # Clean dist directory🤝 Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
📄 License
MIT License - see the LICENSE file for details.
⚠️ Security Considerations
- Code Validation: Always keep
validateCode: truein production - API Restrictions: Be careful when enabling
allowBrowserAPIs - Input Sanitization: Validate all dynamic code input from external sources
- Timeout Settings: Set appropriate timeouts to prevent infinite loops
- Principle of Least Privilege: Only import the minimum required functions/classes
💡 Best Practices
- Storage Quota: Monitor storage usage and implement cleanup strategies
- Version Control: Use content-based hashing (MD5, SHA-256) for digests
- Error Handling: Wrap module creation in try-catch blocks
- Parallel Loading: Use
checkFreshScripts()with multiple modules for better performance - Cache Invalidation: Check for updates on app startup
📞 Support
For issues and feature requests, please use the GitHub Issues page.
