Skip to content

Hybrid Overview

@lentystyle/hybrid is the integration layer between the compiler and the runtime. It does not replace either one; it coordinates generated CSS, manifest or payload artifacts, and optional HTML rewrite flow on the Node side.

Compiler -> Hybrid -> Runtime

  • Compiles .luis sources in global, initial, or route-aware mode
  • Produces shared generated CSS plus a global manifest or route runtime payload files
  • Runs static-site build and dev integration
  • Provides browser payload dispatch and rescan helpers
  • Provides generic factories for framework adapters
  • Validates source and HTML envelope inputs with @lentystyle/core/guard/hybrid before compilation starts

Hybrid runs @lentystyle/core/guard/hybrid before compilation.

In HTML-based flows this adapter:

  • uses the balanced preset by default
  • enforces htmlMode: 'hybrid'
  • keeps framework dev scripts outside the SSR-only script allowlist
  • can stop invalid route HTML or source envelope inputs before compilation starts

In config-driven build and dev flows you do not enable guard separately; hybrid calls it for you.

import { runHybridProjectBuild } from '@lentystyle/hybrid'
await runHybridProjectBuild()

The built-in policy depends on the active build mode:

  • global: preset: 'trusted-build', allowedLocalRoots: sourceRoots
  • initial and route-aware HTML: preset: 'balanced', htmlMode: 'hybrid'

The hybrid block in luis.config.mjs currently does not expose a guardPolicy field. If a custom policy is needed, you need to drop down to the low-level compileHybridBundle() API.

import { compileHybridBundle } from '@lentystyle/hybrid/node'
await compileHybridBundle({
routeId: '/docs/',
html: '<html><head></head><body><div class="card"></div></body></html>',
guardPolicy: {
preset: 'strict',
htmlMode: 'hybrid',
allowedLocalRoots: ['D:/project/public/styles'],
allowedRemoteImportOrigins: ['https://cdn.example.com'],
},
sources: [
{
sourceId: '/styles/docs.luis',
source: '.card { color: #0f172a; }',
},
],
})

This path is for situations where you want stricter or looser validation while keeping low-level hybrid compile behavior.


The recommended path for Node-based projects is a config file and a single build command.

  1. Install @lentystyle/hybrid
  2. Create luis.config.mjs in the project root
  3. Define buildMode, sourceRoots, luisOutputDir, and related settings
  4. Add the lentystyle-hybrid build script
  5. Reuse the same config on the dev middleware or Vite plugin side
import { defineLuisConfig } from '@lentystyle/core/config'
export default defineLuisConfig({
hybrid: {
buildMode: 'initial',
sourceRoots: ['./public', './dist'],
luisOutputDir: './dist/_hybrid',
assetBaseUrl: '/_hybrid',
globalCssFileName: 'lentystyle-global.css',
defaultRuntimeOptions: 'debug,performance',
},
})
{
"scripts": {
"build": "astro build && pnpm exec lentystyle-hybrid build"
}
}

The pnpm exec lentystyle-hybrid build command:

  • reads luis.config.mjs
  • buildMode: 'initial' (default): scans route HTML, writes non-observed plus initially matched ?/?! observed rules into one shared CSS file, links that generated CSS from source-bearing HTML files, and leaves non-matching observed rules to runtime
  • buildMode: 'global': compiles all .luis sources against empty HTML, writes one shared CSS file and one global manifest, leaves all observed rules to runtime, and injects the generated CSS link when htmlOutputDir is explicit
  • buildMode: 'route': scans route HTML or source candidates, writes route payload artifacts and shared CSS
  • runs guard preflight before compilation
  • writes route-scoped CSS files only when writeRouteCssArtifacts: true is configured (route mode only)

createHybridProjectDevMiddleware() or createHybridProjectDevIntegration():

  • reuses the same config
  • produces in-memory assets
  • serves global or initial mode from one in-memory CSS + manifest
  • rewrites HTML responses per route when buildMode: 'route'
  • keeps guard preflight inside the dev flow

Vite projects can consume the same config via @lentystyle/hybrid/vite.

buildMode: 'initial' (the default) scans route HTML and compiles each route against its actual first HTML snapshot. Non-observed rules and initially matched observed rules go into one shared CSS file; source-bearing HTML files receive that generated CSS link and their original .luis tags are marked as observed-only fallbacks. Observed rules that do not match the first snapshot stay in the runtime manifest.

buildMode: 'global' does not compile against route HTML. It compiles every .luis source against empty HTML and produces one shared CSS and one global manifest file. When htmlOutputDir is explicitly configured and exists, it also writes the generated CSS link and observed-only source markers into route HTML files for first paint. Without route HTML, it stays a source-only artifact build.

buildMode: 'route' enables the route-aware HTML scan. It reads route HTML from htmlOutputDir, which defaults to ./dist, and writes per-route payload artifacts.


Runtime and Guard Separation Inside Hybrid

Section titled “Runtime and Guard Separation Inside Hybrid”

Hybrid deliberately keeps these two roles separate:

  • the browser runtime still handles convenience-oriented payload application
  • @lentystyle/core/guard/hybrid hardens the source and HTML envelope inputs before compilation starts

This is why a hybrid project can keep using browser runtime behavior after build, while the compile boundary stays fail-closed on the Node side.


  • Config for project settings
  • Browser Runtime for browser-side behavior
  • @lentystyle/core/guard/hybrid for the fail-closed validation model