Utility Functions
Helper classes and APIs for managing security, custom markup insertion, and programmatic reactivity.
1. html template tag
Section titled “1. html template tag”Creates a SafeHtml wrapper around a template literal, allowing you to build raw HTML content safely. Parameters inserted are automatically escaped unless they are instances of SafeHtml.
import { html } from 'avenx-core/runtime';
const userContent = "<script>alert('xss')</script>";const element = html`<div class="content">${userContent}</div>`;// Output escapes userContent safely!2. SafeHtml class
Section titled “2. SafeHtml class”A wrapper class designating that a string is verified and safe for raw output. Evaluated directly without escaping inside {{{ ... }}} expressions.
3. HtmlEscaper
Section titled “3. HtmlEscaper”Internal utility class providing character replacement mappings to prevent code injections:
const escaper = new HtmlEscaper();escaper.escape('<h1>Text</h1>');// Returns: <h1>Text</h1>4. Sanitizer
Section titled “4. Sanitizer”A utility class used to escape and clean up templates and dynamic HTML tags by stripping dangerous elements/attributes while preserving safe markup.
Constructor
Section titled “Constructor”import { Sanitizer } from 'avenx-core/runtime';
const sanitizer = new Sanitizer(config);config(optional): An object to customize the allowed HTML tags and attributes.allowedTags(string[]): Custom array of allowed tag names. Defaults to a standard safe set of elements (e.g.,div,span,p,a,img, etc.).allowedAttributes(Record<string, string[]>): Custom mapping of tag names to allowed attribute arrays. Use*to specify attributes allowed globally on all elements.
Methods
Section titled “Methods”sanitize(html)
Section titled “sanitize(html)”Sanitizes an input string containing HTML by filtering it against the allowed tags and attributes configuration. Dangerous elements (like <script>, <style>, <iframe>, etc.) and unsafe URL protocols (like javascript:, data: except for safe image data) are stripped.
Parameters:
html(any): The raw content to sanitize (coerced to a string).
Returns:
string: The sanitized, safe HTML string.
Example
import { Sanitizer } from 'avenx-core/runtime';
const sanitizer = new Sanitizer();
const dirtyHtml = '<div>Hello <script>alert("xss")</script> <a href="javascript:alert(1)">World</a></div>';const cleanHtml = sanitizer.sanitize(dirtyHtml);
console.log(cleanHtml);// Output: <div>Hello <a>World</a></div>4b. formatMessage(code, ...args)
Section titled “4b. formatMessage(code, ...args)”Formats an Avenx error/warning template into a console-ready string without throwing. Use this when you want to log or report a framework message yourself.
import { AvenxErrorCodes, formatMessage } from 'avenx-core/runtime';
logger.warn(formatMessage(AvenxErrorCodes.COMPONENT_INJECT_KEY_NOT_FOUND, 'theme'));// => [AVX_W15] Inject key "theme" was not found in the component provide/inject tree.| Param | Type | Description |
|---|---|---|
code |
string |
An AvenxErrorCodes value (e.g. AVX_W15) |
...args |
any[] |
Values substituted for {0}, {1}, … placeholders in AvenxErrorMessages |
Returns: string in the form [`code`] formatted message.
Unlike constructing new AvenxError(code, ...args), formatMessage never throws—it only builds the text for logger.warn, telemetry, or custom UI.
5. Reactivity API Reference
Section titled “5. Reactivity API Reference”Avenx-JS exposes APIs for programmatically creating reactive state objects and observing reactive values.
The core reactivity APIs include StateFactory, AvenxWatcher, and the AvenxComponent.watch() instance method.
6. StateFactory
Section titled “6. StateFactory”StateFactory creates reactive proxy objects from regular JavaScript objects.
Constructor
Section titled “Constructor”import { StateFactory } from 'avenx-core/runtime';
const stateFactory = new StateFactory();The constructor optionally accepts a proxy handler factory class.
new StateFactory(handlerFactoryClass);handlerFactoryClass(optional): The factory class used to create proxy handlers. Defaults toProxyHandlerFactory.
create(initialState, options)
Section titled “create(initialState, options)”Creates and returns a reactive proxy for the provided state object.
const state = stateFactory.create(initialState, options);Parameters
initialState(object, optional): The initial state object to make reactive. Defaults to an empty object.options(object, optional): Configuration options passed to the proxy handler factory. Defaults to an empty object.
Returns
Proxy: A reactive proxy around the provided state object.
If initialState is already an Avenx reactive proxy, create() returns the existing proxy instead of wrapping it in another proxy.
Example
import { StateFactory } from 'avenx-core/runtime';
const stateFactory = new StateFactory();
const state = stateFactory.create({ count: 0, user: { name: 'Avenx User', },});
state.count++;state.user.name = 'Updated User';Options Schema
Section titled “Options Schema”The options object passed to create(initialState, options) configures the behavior of the created reactive proxy and its underlying ProxyHandlerFactory:
| Option | Type | Default | Description |
|---|---|---|---|
onChange |
Function |
() => {} |
A change notification callback executed whenever any reactive property on the target (or nested reactive child objects, arrays, Sets, or Maps) is modified, set, or deleted. |
computedKeys |
Array<String> |
[] |
An array of property names to treat as dynamic computed properties on the target object. |
instance |
Object |
null |
Optional component or context instance reference passed for scope resolution and method binding. |
bypassSymbol |
Boolean |
false |
When true, prevents StateFactory from defining the internal non-enumerable __avenx_proxy_ref__ symbol property on the target object. |
The onChange Callback API
Section titled “The onChange Callback API”The onChange option allows developers to attach a change listener to a standalone reactive state object created outside of a component lifecycle.
Whenever a property on the reactive state proxy (or any nested reactive object/array) is modified, set, or deleted, onChange is invoked automatically. This enables building custom state management stores, state persistence sync (e.g. with localStorage), or external event logs.
Example: Standalone Reactive Store with onChange Persistence
Section titled “Example: Standalone Reactive Store with onChange Persistence”import { StateFactory } from 'avenx-core/runtime';
const stateFactory = new StateFactory();
// Load initial state from localStorage or fallback defaultsconst savedState = JSON.parse(localStorage.getItem('app_settings') || '{}');
const settingsState = stateFactory.create( { theme: savedState.theme || 'dark', notifications: savedState.notifications ?? true, user: { name: 'Alice', }, }, { onChange() { // Sync state updates to localStorage whenever any property changes localStorage.setItem('app_settings', JSON.stringify({ theme: settingsState.theme, notifications: settingsState.notifications, user: settingsState.user, })); console.log('Settings persisted to localStorage:', settingsState); }, });
// Mutating top-level or nested properties automatically triggers onChangesettingsState.theme = 'light'; // Logs and saves to localStoragesettingsState.user.name = 'Bob'; // Triggers onChange for nested mutations7. AvenxWatcher
Section titled “7. AvenxWatcher”AvenxWatcher observes values returned by reactive getter functions. During getter evaluation, the watcher tracks accessed reactive properties and responds when those dependencies change.
Constructor
Section titled “Constructor”import { AvenxWatcher } from 'avenx-core/runtime';
const watcher = new AvenxWatcher(getter, callback, options);Parameters
getter(function): A function that returns the reactive value or expression to observe.callback(function | null, optional): Called when the watched value changes. The callback receives the new value and previous value.options(object, optional): Configuration options controlling watcher behavior.
Options
Section titled “Options”immediate
Section titled “immediate”{ immediate: true;}When true, the callback runs immediately after the initial value is evaluated.
The initial callback receives the current value as the first argument and undefined as the previous value.
{ lazy: true;}When true, the initial getter evaluation is postponed until the watcher is evaluated.
Properties
Section titled “Properties”getter— The reactive evaluation function supplied to the constructor.callback— The callback function invoked when the watched value changes.options— The watcher configuration object.deps— ASetcontaining the reactive dependencies tracked by the watcher.dirty— A boolean indicating whether a lazy watcher needs to be re-evaluated.value— The currently stored value returned by the getter.
Methods
Section titled “Methods”Evaluates the getter inside the active watcher context and tracks reactive dependencies.
const value = watcher.get();evaluate()
Section titled “evaluate()”Evaluates a lazy watcher when it is dirty and returns the stored value.
const value = watcher.evaluate();update()
Section titled “update()”Re-evaluates the watcher when one of its tracked dependencies changes.
For non-lazy watchers, the callback runs when the value changes or when the evaluated value is an object.
For lazy watchers, the watcher is marked as dirty.
teardown()
Section titled “teardown()”Removes the watcher from all tracked dependencies and clears its dependency collection.
watcher.teardown();Use teardown() when manually managing an AvenxWatcher instance that is no longer needed.
8. AvenxComponent.watch()
Section titled “8. AvenxComponent.watch()”Every AvenxComponent instance provides a watch() method for observing reactive values programmatically.
Signature
Section titled “Signature”this.watch(getter, callback, options);Parameters
getter(function): A function returning the reactive value to observe.callback(function): Called when the watched value changes. ReceivesnewValueandoldValue.options(object, optional): Watcher configuration options such asimmediateandlazy.
Returns
AvenxWatcher: The watcher instance created for the component.
Watchers registered with this.watch() are stored by the component and automatically cleaned up when the component is unmounted.
Watching Dynamic State
Section titled “Watching Dynamic State”The getter function determines which reactive state properties should be tracked.
import { AvenxComponent } from 'avenx-core/runtime';
class CounterComponent extends AvenxComponent { constructor() { super({ count: 0, });
this.watch( () => this.state.count, (newValue, oldValue) => { console.log(`Count changed from ${oldValue} to ${newValue}`); }, ); }}Whenever state.count changes, the getter is re-evaluated and the callback receives the new and previous values.
Using the immediate Option
Section titled “Using the immediate Option”Set immediate to true to execute the callback immediately with the initial value.
this.watch( () => this.state.count, (newValue, oldValue) => { console.log('Current count:', newValue); }, { immediate: true, },);During the initial callback, oldValue is undefined.
Watching Dynamic Dependencies
Section titled “Watching Dynamic Dependencies”Watchers track reactive properties that are accessed while the getter executes. This allows the watched dependency to change dynamically.
this.watch( () => { return this.state.usePrimary ? this.state.primaryValue : this.state.secondaryValue; }, (newValue, oldValue) => { console.log('Selected value changed:', newValue, oldValue); },);The getter observes usePrimary and accesses either primaryValue or secondaryValue based on the current state.
Cleaning Up Watchers
Section titled “Cleaning Up Watchers”Watchers created with this.watch() are automatically cleaned up when the component is unmounted.
When creating an AvenxWatcher manually, call teardown() when the watcher is no longer required:
const watcher = new AvenxWatcher( () => state.count, (newValue, oldValue) => { console.log(newValue, oldValue); },);
watcher.teardown();AvenxLogger
Section titled “AvenxLogger”The AvenxLogger class provides Avenx-JS’s built-in logging system. It supports configurable log levels, custom formatting, and custom transports, making it suitable for both development and production environments.
A shared logger instance is also exported from the runtime for convenient use throughout your application.
Importing
Section titled “Importing”import { AvenxLogger, logger } from "avenx-core/runtime";AvenxLoggercreates a new logger instance with custom configuration.loggeris the default global logger instance provided by Avenx-JS.
Constructor
Section titled “Constructor”const logger = new AvenxLogger(config);Configuration Options
Section titled “Configuration Options”| Option | Type | Default | Description |
|---|---|---|---|
level |
string |
"info" |
Minimum log level to output. |
silent |
boolean |
false |
Disables all logging when enabled. |
formatter |
Function |
defaultFormatter |
Formats log messages before they are passed to transports. |
transports |
Array |
[consoleTransport] |
Collection of custom transport targets. |
Log Levels
Section titled “Log Levels”Supported log levels are listed below in ascending order of severity.
| Level | Description |
|---|---|
trace |
Detailed diagnostic information. |
debug |
Development and debugging messages. |
info |
General application information. |
warn |
Warning messages. |
error |
Error messages. |
fatal |
Critical failures. |
off |
Disables logging. |
silent |
Alias for off. |
The logger only outputs messages whose severity is greater than or equal to the configured log level.
Logging Methods
Section titled “Logging Methods”Every logger instance provides the following methods.
| Method | Description |
|---|---|
trace(...args) |
Logs a trace message. |
debug(...args) |
Logs a debug message. |
info(...args) |
Logs an informational message. |
log(...args) |
Alias for info(). |
warn(...args) |
Logs a warning. |
error(...args) |
Logs an error. |
fatal(...args) |
Logs a fatal error. |
Using the Global Logger
Section titled “Using the Global Logger”The runtime exports a shared logger instance that can be used anywhere in your application.
import { logger } from "avenx-core/runtime";
logger.info("Application started.");logger.warn("Cache miss.");logger.error("Failed to load configuration.");This is the same instance that AvenxApp configures when you pass a logging option to its constructor, so logger.configure() calls and the logging option affect the same shared state:
const app = new AvenxApp({ target: "#app", logging: { level: "debug" },});Note that this is unrelated to the logging option in avenx.config.json, which only controls the CLI’s own build-time output, not this runtime logger.
Creating a Custom Logger
Section titled “Creating a Custom Logger”Create a separate logger instance with its own configuration.
import { AvenxLogger } from "avenx-core/runtime";
const logger = new AvenxLogger({ level: "debug"});
logger.debug("Debug logging enabled.");Updating Logger Configuration
Section titled “Updating Logger Configuration”Logger instances can be reconfigured at runtime.
import { logger } from "avenx-core/runtime";
logger.configure({ level: "trace"});
logger.trace("Verbose logging is now enabled.");You can update one or more configuration options at any time using configure().
If level is set to a value that isn’t one of the supported log levels, configure() logs a warning and falls back to "info".
Custom Formatter
Section titled “Custom Formatter”A formatter receives the log level and the original arguments, then returns the formatted arguments passed to each transport.
const formatter = (level, args) => [ `[MyApp] [${level.toUpperCase()}]`, ...args];
const logger = new AvenxLogger({ formatter});Custom Transport
Section titled “Custom Transport”By default, AvenxLogger uses consoleTransport, which dispatches each level to a console method: fatal logs via console.error, trace logs via console.debug, and every other level logs via the matching console method (e.g. info → console.info), falling back to console.log if no matching method exists.
Custom transports allow log messages to be forwarded to destinations other than the browser console.
A transport may be either:
- an object exposing a
log()method - a function
Object Transport
Section titled “Object Transport”const transport = { log(level, formattedArgs, rawArgs) { console.log("Sending log:", formattedArgs); }};
const logger = new AvenxLogger({ transports: [transport]});Function Transport
Section titled “Function Transport”const transport = (level, formattedArgs, rawArgs) => { console.log(level, formattedArgs);};
const logger = new AvenxLogger({ transports: [transport]});Example
Section titled “Example”import { logger } from "avenx-core/runtime";
logger.info("Application initialized.");
logger.debug("Loaded configuration.");
logger.warn("Using default settings.");
logger.error("Unable to connect to the server.");
logger.fatal("Unexpected unrecoverable error.");LruCache Utility Class
Section titled “LruCache Utility Class”LruCache is a Least Recently Used (LRU) cache implementation built using JavaScript Map’s key insertion order preservation. It is used internally for features like page keep-alive caching and is exported from avenx-core/runtime for application-level data caching.
Constructor
Section titled “Constructor”import { LruCache } from 'avenx-core/runtime';
const cache = new LruCache(limit, onEvict);| Parameter | Type | Default | Description |
|---|---|---|---|
limit |
number |
Required | Maximum number of items allowed in the cache. Must be a positive number (> 0). |
onEvict |
(key: string, value: any) => void |
null |
Optional callback function invoked whenever an item is evicted due to exceeding capacity. |
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
limit |
number |
Capacity limit of the cache instance. |
size |
number |
Getter returning the current count of items stored in the cache. |
Methods
Section titled “Methods”get(key)
Section titled “get(key)”Retrieves an item from the cache and updates its recency to make it the most recently used item.
- Parameters:
key: string - Returns:
any— The cached item, orundefinedif the key does not exist.
set(key, value)
Section titled “set(key, value)”Inserts or updates a key-value pair in the cache. If the cache size reaches the specified limit, the least recently used (LRU) item is evicted and the optional onEvict callback is triggered.
- Parameters:
key: string— Item identifier.value: any— Data payload to cache.
- Returns:
void
has(key)
Section titled “has(key)”Checks whether a key exists in the cache without altering its recency ordering.
- Parameters:
key: string - Returns:
boolean—trueif the key exists, otherwisefalse.
delete(key)
Section titled “delete(key)”Removes a specific item from the cache.
- Parameters:
key: string - Returns:
boolean—trueif the item existed and was removed, otherwisefalse.
clear()
Section titled “clear()”Removes all items from the cache.
- Returns:
void
Usage Example
Section titled “Usage Example”import { LruCache } from 'avenx-core/runtime';
// Create a cache holding up to 3 items with an eviction listenerconst userCache = new LruCache(3, (evictedKey, evictedValue) => { console.log(`Cache full. Evicted key "${evictedKey}":`, evictedValue);});
// Store itemsuserCache.set('user:101', { name: 'Alice', role: 'admin' });userCache.set('user:102', { name: 'Bob', role: 'editor' });userCache.set('user:103', { name: 'Charlie', role: 'viewer' });
console.log(userCache.size); // 3
// Accessing 'user:101' refreshes its recencyconst user = userCache.get('user:101');console.log(user.name); // 'Alice'
// Inserting a 4th item triggers LRU eviction of 'user:102' (since 'user:101' was recently accessed)userCache.set('user:104', { name: 'Diana', role: 'manager' });// Output: Cache full. Evicted key "user:102": { name: 'Bob', role: 'editor' }
console.log(userCache.has('user:102')); // falseconsole.log(userCache.has('user:101')); // true