Skip to content

Latest commit

 

History

History
252 lines (184 loc) · 8.66 KB

File metadata and controls

252 lines (184 loc) · 8.66 KB

Configuration & Security

Global Settings

<script>
  NoJS.config({
    // API
    baseApiUrl: 'https://api.myapp.com/v1',
    headers: { 'Authorization': 'Bearer xxx' },
    timeout: 10000,
    retries: 2,
    retryDelay: 1000,
    credentials: 'include',    // fetch credentials mode

    // CSRF
    csrf: {
      header: 'X-CSRF-Token',
      token: '...'
    },

    // Caching
    cache: {
      strategy: 'memory',     // 'none' | 'memory' | 'session' | 'local'
      ttl: 300000              // 5 minutes
    },

    // Templates
    templates: {
      cache: true             // Cache fetched .tpl HTML in memory (default: true)
    },

    // Router
    router: {
      useHash: false,          // true = hash mode, false = history mode (default)
      base: '/',
      scrollBehavior: 'top',  // 'top' | 'preserve' | 'smooth'
      templates: 'pages',       // Default base path for file-based routing (default: 'pages')
      ext: '.tpl'              // Default file extension for file-based routing (fallback: '.html')
    },
    // Note: Anchor links (href="#id") are automatically
    // intercepted in both modes — they scroll to the target
    // element without triggering route navigation.

    // i18n
    i18n: {
      defaultLocale: 'en',
      fallbackLocale: 'en',
      detectBrowser: true,
      loadPath: '/locales/{locale}.json',  // Load from external JSON (default: null)
      ns: ['common'],           // Namespaces to preload (default: [])
      cache: true               // Cache fetched locale files (default: true)
    },

    // Debugging
    debug: true,               // Logs directive processing
    devtools: true,            // Enables browser devtools panel

    // Security
    sanitize: true,            // Sanitize bind-html

    // Performance
    exprCacheSize: 500,        // Max entries in the expression/statement LRU caches
  });
</script>

Pre-initializing Stores

Use stores in NoJS.config() to create multiple global stores before the DOM is processed. This is useful when stores need to exist before any HTML directive runs — for example, when setting auth state from a JWT, or hydrating from localStorage.

<script>
  NoJS.config({
    stores: {
      auth:  { user: null, token: localStorage.getItem('token') },
      cart:  { items: [], total: 0 },
      theme: { mode: 'dark', accent: 'blue' }
    }
  });
</script>

Stores created via config() behave exactly like those declared with the store HTML attribute — they are reactive contexts accessible via $store.name in expressions and NoJS.store.name in JavaScript.

If a store name already exists, config() will not overwrite it. This means you can safely call config() multiple times without resetting store data.


Request Interceptors

<script>
  // Before every request
  NoJS.interceptor('request', (url, options) => {
    options.headers['X-Request-ID'] = crypto.randomUUID();
    return options;
  });

  // After every response
  NoJS.interceptor('response', (response, url) => {
    if (response.status === 401) {
      NoJS.store.auth.user = null;
      NoJS.notify(); // flush DOM bindings before redirect
      NoJS.router.push('/login');
      throw new Error('Unauthorized');
    }
    return response;
  });
</script>

Security

XSS Protection

  • bind always sets textContent, never innerHTML — safe by default.
  • bind-html sanitizes content using a DOMParser-based structural sanitizer. Regex-based sanitizers are bypassable via SVG/MathML event handlers and entity encoding — DOMParser resolves entities first, making all vectors uniformly detectable.
  • Template expressions are evaluated by a custom sandboxed parser — no eval() or Function() is used, and dangerous properties like __proto__ and constructor are blocked.

Custom sanitizer hook (sanitizeHtml)

To use an external sanitizer like DOMPurify instead of the built-in one:

NoJS.config({
  sanitizeHtml: (html) => DOMPurify.sanitize(html)
});

When sanitizeHtml is set to a function, the built-in sanitizer is bypassed entirely and the provided function is used for all bind-html content. Set sanitize: false to disable sanitization entirely (not recommended — see Security).

The built-in sanitizer blocks the following tags by default: script, style, iframe, object, embed, base, form, meta, link, noscript. It also strips on* event handler attributes and javascript:/vbscript: scheme attributes on any element, and data: URIs on URL attributes (href, src, action) unless they are safe image types.

CSRF Protection

<script>
  NoJS.config({
    csrf: {
      header: 'X-CSRF-Token',
      token: document.querySelector('meta[name="csrf-token"]').content
    }
  });
</script>

Content Security Policy

No.JS uses a custom expression parser that is fully CSP-compliant — no eval() or Function() constructor is used. No unsafe-eval directive is required in your Content Security Policy.


templates.cache

Type: boolean | Default: true

Controls whether the HTML content of remotely-fetched .tpl files is stored in an in-memory Map after the first request. On repeated navigations to the same route, the cached HTML is used directly — no HTTP request is made. The cache lives for the duration of the page session (no TTL — template assets are static, not data).

// Disable template caching (always re-fetch .tpl files)
NoJS.config({ templates: { cache: false } });

// Default — caching is on, no configuration needed
NoJS.config({ templates: { cache: true } });

Set to false during local development if you want changes to .tpl files to be reflected without a hard page reload.


i18n.loadPath

Type: string | null | Default: null

URL template for loading locale JSON files via fetch. Use {locale} and optionally {ns} as placeholders. When null, translations must be provided inline via NoJS.i18n({ locales }).

NoJS.i18n({
  loadPath: '/locales/{locale}.json'          // Flat mode
  loadPath: '/locales/{locale}/{ns}.json'   // Namespace mode
});

i18n.ns

Type: string[] | Default: []

Array of namespace identifiers to preload at init(). Each namespace corresponds to a separate JSON file per locale. Additional namespaces can be loaded on-demand via the i18n-ns directive or route attribute.

NoJS.i18n({
  loadPath: '/locales/{locale}/{ns}.json',
  ns: ['common', 'auth']
});

i18n.cache

Type: boolean | Default: true

Controls whether fetched locale JSON files are stored in an in-memory Map after the first request. Set to false during development for hot-reload of translation files.

NoJS.i18n({ cache: false }); // Always re-fetch locale files

exprCacheSize

Type: number | Default: 500

Maximum number of entries in each of the two internal LRU caches used by the expression evaluator: one for parsed expression ASTs (_exprCache) and one for parsed statement ASTs (_stmtCache). When the limit is reached the least-recently-used entry is evicted to make room.

The default of 500 is suitable for most applications. Increase it if your app evaluates a large number of distinct template expressions (e.g. a dynamic form with hundreds of unique field bindings). Decrease it to reduce memory pressure in memory-constrained environments.

// Larger cache for apps with many distinct expressions
NoJS.config({ exprCacheSize: 1000 });

// Smaller cache for memory-constrained environments
NoJS.config({ exprCacheSize: 100 });

Non-positive or non-numeric values are ignored and the default of 500 is used.


Plugin System

No.JS includes a plugin system for extending the framework with reusable packages. The following methods are available on the NoJS object:

Method Description
NoJS.use(plugin, options?) Register a plugin. Accepts an object with { name, install } or a named function
NoJS.global(name, value) Inject a reactive variable accessible as $name in templates
NoJS.dispose() Full app teardown — disposes plugins in reverse order, clears globals and interceptors
NoJS.CANCEL Sentinel Symbol — return from a request interceptor to abort the request
NoJS.RESPOND Sentinel Symbol — return from a request interceptor to serve a cached response
NoJS.REPLACE Sentinel Symbol — return from a response interceptor to replace the response data

See Plugins → for the full API reference, plugin lifecycle, security guidelines, and examples.


Next: Directive Cheatsheet →