Module map

ModulePathContents
vesta.mjs/static/framework/vesta.mjsXHR, reactive state, utility functions
navigation.mjs/static/framework/navigation.mjsPage navigation, menus, modals, steps
websockets.mjs/static/framework/websockets.mjsWebSocket client with auto-reconnect
global.mjs/static/framework/global.mjsShared application state object
utils.mjs/static/framework/utils.mjsMiscellaneous helpers
markdown.mjs/static/markdown/markdown.mjsCustom markdown-to-HTML renderer
translation.mjs/static/translations/translation.mjsi18n string loader

Import what you need in your entry point (static/main.mjs):

import { xhr, fillWith, Subscribe, updateElement } from '/static/framework/vesta.mjs';
import { goTo, toggleMenu, toggleFM } from '/static/framework/navigation.mjs';
import G from '/static/framework/global.mjs';

HTTP requests — xhr()

xhr(endpoint, effect, method="GET", async=true, body)
Wrapper around XMLHttpRequest. Calls effect(responseText) when the request completes. Use this instead of raw fetch to stay consistent with the reactive system.
ParameterTypeDescription
endpointstringURL, e.g. "/api_posts?page=2"
effectfunctionCallback receiving the response string
methodstring"GET" (default) or "POST"
asyncboolDefaults to true
bodystring | nullRequest body for POST
// Simple GET
xhr('/api_posts', (raw) => {
    const posts = JSON.parse(raw);
    console.log(posts);
});

// POST with JSON body
xhr('/api_create', (raw) => {
    const result = JSON.parse(raw);
    if (result.ok) updateElement('postList', ...);
}, "POST", true, JSON.stringify({ title, content }));

Global state — global.mjs

A single plain object exported from global.mjs. Import it anywhere — it's the same reference everywhere (ES module singleton).

import G from '/static/framework/global.mjs';

// Write
G.user = { id: 42, name: "Alice" };
G.posts = [];

// Read anywhere
console.log(G.user.name);

Typical top-level shape used with Subscribe:

// global.mjs — initialise your state shape here
const G = {
    user:     null,
    posts:    [],
    settings: {},
    i18n:     {},
};
export default G;

Reactive rendering

Subscribe

Subscribe(element, content, className, params)
Bind a DOM element to a state variable. Whenever the variable changes via updateElement / setElement etc., the element re-renders.
ParameterDescription
elementCSS selector string targeting the DOM element
contentFunction returning an HTML string. Receives the current state value. Escape any untrusted data with escapeHTML() (see Output escaping).
classNameName of the state variable in G (or any namespace)
paramsOptions: {target: "html"|"style"|"class", element: stateVar, repaint: fn}
import G from '/static/framework/global.mjs';
import { Subscribe, updateElement } from '/static/framework/vesta.mjs';

// Bind #post-list to G.posts — escapeHTML() guards the untrusted title against XSS
Subscribe('#post-list', (posts) => {
    return posts.map(p => `<div class="post">${escapeHTML(p.title)}</div>`).join('');
}, 'posts', { target: 'html', element: G.posts });

// Later — update state and re-render automatically
G.posts = [{ title: "Hello" }, { title: "World" }];
updateElement('posts', G.posts);

State mutation helpers

updateElement(key, value)
Assign value to state key and re-render all subscribers of that key.
setElement(element, value)
Update a single-value state variable and re-render.
pushElement(element, value)
Append value to an array state variable and re-render.
addElement(element, value)
Merge value into an object state variable and re-render.
deleteElement(element, index)
Remove item at index from an array state variable and re-render.
deleteElementDict(element, key)
Delete key from an object state variable and re-render.
deleteVal(element, val)
Remove first occurrence of val from an array and re-render.

HTML templates — fillWith()

fillWith(template, list)
Fetch /static/templates/{template}.html and render it once per item in list. Returns a promise that resolves to the concatenated HTML string. The template is a JS template literal — the item is exposed as element.
// static/templates/post.html
`<div class="post-card">
  <h3>${element.title}</h3>
  <p>${element.content}</p>
</div>`
import { fillWith } from '/static/framework/vesta.mjs';

const markup = await fillWith('post', posts);
document.getElementById('list').innerHTML = markup;

Output escaping (XSS)

Interpolation in templates (fillWith, getTemplate, loadTemplate, and Subscribe content) is raw by default — the engine stays flexible so you can compose markup freely. That means you are responsible for escaping any untrusted value before it lands in the DOM.

// Trusted / your own markup — interpolate freely:
`<ul>${fillWith('row', items)}</ul>`

// UNTRUSTED data (user input, query params, message bodies) — wrap in escapeHTML():
`<p>${escapeHTML(user.name)}</p>`
Security guideline: always run user-controlled data through escapeHTML() (available globally) when interpolating it into a template. Forgetting to escape untrusted input is an XSS vulnerability. Use raw interpolation only for markup you control.

Navigation

All navigation helpers are in navigation.mjs.

goTo(id, target, selected, async, postInsert)
Load template target and inject it into element id. selected tracks the active item: {category: "nav", id: "home"}. postInsert is an optional callback run after injection.
openMenu(id, async)
Show a menu element. If async is true, loads the template first.
toggleMenu(id, async, target)
Toggle visibility of a menu. Loads template target on first open.
toggleFM(id)
Toggle a floating menu. Only one floating menu can be open at a time — opening one automatically closes the previous.
closeFM()
Close the currently open floating menu.
toggleModale(id, event)
Show a modal positioned at the mouse cursor. event is the click event.
gotoStep(step, stepsID)
Navigate a multi-step form or wizard. Moves the CSS transform of element stepsID to show step step (0-indexed).
closeMenu(cible, id)
Hide element cible and reset the active state for id.
hideLoadingScreen()
Fade out and remove the initial loading screen. Call this once your app is ready.
printWatermark(name, git)
Print an ASCII-art watermark with the app name and git URL to the console.
import { goTo, toggleFM, gotoStep, hideLoadingScreen } from '/static/framework/navigation.mjs';

// Navigate to a section
document.getElementById('btn-posts').addEventListener('click', () => {
    goTo('main-content', 'posts', { category: 'nav', id: 'posts' });
});

// Multi-step form
document.getElementById('btn-next').addEventListener('click', () => {
    gotoStep(currentStep + 1, 'wizard');
});

// Init
hideLoadingScreen();

WebSocket client

initWebSockets()
Connect to the WebSocket server. The URL is fetched automatically from /config. Authenticates with /authWS then hands off incoming messages to /static/ws/onMessage.mjs.
// static/ws/onMessage.mjs — you write this
export function onMessage(data) {
    const msg = JSON.parse(data);
    switch (msg.type) {
        case 'notification':
            pushElement('notifications', msg.content);
            break;
        case 'update':
            updateElement('feed', msg.payload);
            break;
    }
}
// main.mjs
import { initWebSockets } from '/static/framework/websockets.mjs';

initWebSockets(); // call once at startup

Internationalisation

Translation files live at static/translations/{lang}.mjs and export a plain object of key → translated string.

// static/translations/en.mjs
export default {
    welcome: "Welcome",
    logout:  "Sign out",
    save:    "Save changes",
};

// static/translations/fr.mjs
export default {
    welcome: "Bienvenue",
    logout:  "Se déconnecter",
    save:    "Enregistrer",
};
import { loadTranslations, t } from '/static/translations/translation.mjs';

await loadTranslations('fr');
console.log(t('welcome')); // → "Bienvenue"

// In a template
`<button>${t('save')}</button>`

Utility functions

logout()
POST to /logout and redirect to /auth.
goodbye()
Show a confirmation dialog then POST to /goodbye to delete the account.
checkEnter(event, effect)
Call effect() when the user presses Enter. Use on input keydown handlers.
getTimeStr(timestamp, options)
string
Format a Unix timestamp as a locale-aware date/time string. options is passed to Intl.DateTimeFormat.
generateUUID()
string
Generate a RFC 4122 UUID v4.
difference(arrKeys, dict)
string[]
Return keys that are in arrKeys but not in dict. Useful for diffing.
import { checkEnter, getTimeStr, generateUUID } from '/static/framework/vesta.mjs';

// Submit on Enter
input.addEventListener('keydown', (e) => checkEnter(e, submitForm));

// Format a timestamp
const label = getTimeStr(post.created_at, { dateStyle: 'medium', timeStyle: 'short' });

// Generate an ID
const id = generateUUID(); // "550e8400-e29b-41d4-a716-446655440000"

Markdown renderer

A custom client-side parser. Syntax differs slightly from standard Markdown — designed for user-generated content with safe rendering.

import { render } from '/static/markdown/markdown.mjs';

const html = render("**bold** and _italic_ text\n\n> a blockquote");
document.getElementById('content').innerHTML = html;
SyntaxOutput
**text**Bold
_text_Italic
~~text~~Strikethrough
`code`Inline code
```…```Code block
> textBlockquote
||text||Spoiler (hidden until click)
[label](url)Link
Note The renderer sanitises output. Arbitrary HTML inside markdown is escaped, not rendered.