npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

energo

v1.0.0-beta.11

Published

Reference-grade Solid + Vite framework for Bun: file-based App Router, document-owned root layouts, render-to-string-only SSR, route-level data and assets, middleware/API routes, SSG.

Readme

energo

A fast, minimal Solid + Vite framework built for Bun.

File-based routing, server-side rendering, route-level data, API routes, middleware, static-site generation — one small, strictly typed package with zero runtime dependencies.

npm license runtime types

Why Energo

  • Bun-native speed. The server runs directly on Bun.serve — no Node compatibility layers, no adapters, no hidden overhead on the hot path.
  • Zero runtime dependencies. The published package depends on nothing at runtime. What you install is what runs.
  • Routing compiled ahead of time. Route plans are generated at build time — no filesystem scanning and no route resolution cost per request.
  • Predictable SSR. Synchronous renderToString only: simple mental model, no streaming edge cases, HTML goes out fast.
  • Strict by default. Fail-closed validation everywhere — config, routing, cookies, redirects and static export reject invalid input with clear, prefixed errors instead of guessing.
  • A deliberately small API. Every export lives on an explicit subpath. No root import, no barrel files, no index.ts maze — tree-shakeable ESM all the way.

| Layer | Technology | | -------- | -------------------------- | | Runtime | Bun ≥ 1.3.13 | | UI | Solid (solid-js) | | Bundler | Vite + vite-plugin-solid | | Language | TypeScript (strict) |

Quick start

Install the framework and its peers:

bun add -d energo solid-js vite vite-plugin-solid

energo does not depend on typescript, at any version. Add it for your own type checking if you want it.

Register the plugin in vite.config.ts:

import { defineConfig } from 'vite'
import { energo } from 'energo/vite'

export default defineConfig({
  plugins: [energo()],
})

Create a root layout and your first page. The root layout owns the whole document — it renders <html>, <head> and <body> itself. The entire document hydrates as one tree, so signals, event handlers and <Link> navigation work anywhere — including a header rendered by the root layout:

// src/app/routes/layout.tsx
import type { JSX } from 'solid-js'

export default function RootLayout(props: { children: JSX.Element }): JSX.Element {
  return (
    <html lang="en">
      <head />
      <body>{props.children}</body>
    </html>
  )
}

Document assembly validates that root-layout output is one unambiguous HTML document before framework head, payload and entry markup is inserted. Its private scanner is tokenizer-aware but deliberately accepts a constrained document grammar rather than claiming to implement the browser's complete HTML tree builder. Root-layout elements must be properly nested and explicitly closed, and direct <head> content is limited to metadata elements that remain in the HTML in head insertion mode. Only <head> and <body> may be direct <html> element children. The scanner also models states that can hide apparent structure, including escaped/double-escaped scripts, comments, foreign content and integration points. A root layout must not use <plaintext>, must not hide document structural tags inside <template>, and must have the same document structure whether <noscript> is parsed with scripting enabled or disabled; ambiguous output fails closed with an [energo] render error. This proves the direct browser ownership of framework-inserted head and body anchors; application markup inside those anchors must still be conforming HTML.

// src/app/routes/page.tsx
import { Title } from 'energo/head'

export default function HomePage() {
  return (
    <main>
      <Title>Hello Energo</Title>
      <h1>Hello Energo</h1>
    </main>
  )
}

Run it:

energo dev      # development server with HMR
energo build    # production build + SSG
energo start    # serve the production build

Routing

Routes live under src/app/routes/. Directories define URL segments, files define what a segment renders or handles:

| File | Role | | --------------- | ------------------------------------------------------------------ | | layout.tsx | Shared UI for a branch; receives props.children | | page.tsx | The routable UI for a segment | | error.tsx | Error boundary view; receives props.error ({ message, name? }) | | not-found.tsx | 404 view for the branch | | route.ts | API endpoint (exports HTTP method handlers) | | middleware.ts | Branch-scoped middleware |

Solid represents JSX-valued component props as lazy accessors. Render an optional slot directly when it is read once. If a slot is read conditionally or more than once, resolve it through Solid's children() helper first:

import { Show, children } from 'solid-js'

function Shell(props: { header?: JSX.Element; children: JSX.Element }) {
  const header = children(() => props.header)
  return (
    <section>
      <Show when={header()}>{header()}</Show>
      {props.children}
    </section>
  )
}

Do not use an unresolved JSX subtree as both the condition and content, such as <Show when={props.header}>{props.header}</Show>. That asks Solid control flow to materialize component output as condition data and can break SSR hydration.

Segment naming:

| Directory | Matches | params | | ------------- | ----------------------------- | ----------------------------- | | blog | /blog | — | | [slug] | /post-1 | slug: string | | [...rest] | /a/b/c (one or more) | rest: string[] | | [[...rest]] | / and /a/b/c (zero+) | rest: string[] \| undefined | | (marketing) | nothing — grouping only | — | | _internal | nothing — private, not routed | — |

Only the route files and segment forms listed above are conventions. loading.tsx, server-companion or alternate-extension route files, metadata/RSC route suffixes, parallel @slot segments, intercepting segments and optional single parameters fail the build instead of being silently ignored or treated as static routes. The /_energo namespace and leading Bun route markers are reserved. Catch-all segments must be terminal, optional catch-alls own their base path, and parameter names or transparent groups do not distinguish otherwise identical URL shapes.

Parameter names are Energo identifiers, not Bun route tokens. They may contain Unicode, emoji, combining characters, hyphens, or begin with a digit (for example [naïve], [💥], or [9id]); names must be non-empty, unambiguous, and unique within one pattern. Energo preserves these exact keys in params and uses private positional aliases only at the Bun transport boundary. At the matching boundary, route params are own properties of a null-prototype record, so names such as __proto__, constructor, and toString remain exact data keys in server contexts and client route state. Optional catch-all base routes expose their exact key with the value undefined.

Static route directory names use one canonical URL spelling. Literal route text is ASCII RFC 3986 pchar data; non-ASCII text must be UTF-8 percent-encoded with uppercase hex (caf%C3%A9, not café or caf%c3%a9). Unreserved characters must stay literal (A, not %41), and a literal static segment cannot begin with : or *.

Params are URL-decoded once with tolerant Web semantics; malformed percent-encoding stays a literal value instead of breaking the request. A URL can be owned by either a page or an API route, never both — collisions fail the build.

When several traversable branches expose a not-found.tsx, Energo selects one deterministically: greater structural depth wins, then the complete segment vector is compared left-to-right with static segments above dynamic segments and dynamic segments above catch-alls. Route groups do not affect this rank. The same selection is used for hard loads, both route-data endpoint forms and managed browser navigation; an equal-rank ambiguity fails closed.

During development, adding, removing or renaming a documented route convention publishes one atomic route revision across page, API, middleware and both route-data transports. A rejected revision leaves the previous accepted routes available, and fixing the source resumes regeneration without restarting the Vite server. Required and optional catch-all ownership changes follow the same rule.

Data loading

Colocate a loader with a page or layout. It runs on the server for the initial HTML and for client-side navigation data requests:

// src/app/routes/blog/[slug]/page.tsx
import { useServerData } from 'energo/router/hooks'
import { notFound } from 'energo/router/not-found'
import { redirect } from 'energo/router/redirect'

import type { GetServerData } from 'energo/router/types'

interface PostData {
  title: string
}

export const getServerData: GetServerData<PostData> = async (ctx) => {
  if (ctx.params['slug'] === 'legacy') return redirect('/blog', 308)
  const post = await loadPost(String(ctx.params['slug']), ctx.signal)
  if (post === undefined) return notFound()
  ctx.response.headers.set('Cache-Control', 'public, max-age=60')
  return { title: post.title }
}

export default function PostPage() {
  const data = useServerData<PostData>()
  return <h1>{data()?.title}</h1>
}

The loader context carries params, url, request, headers, cookies (read-only), locals, response (mutable status + headers) and signal. If the client disconnects while a loader or API handler is still running, that request-owned signal aborts; cancellation is control flow and is not reported through server.onError. A loader returns finite plain JSON data, bounded sparse arrays, redirect(url, status?), notFound() or undefined. Sparse arrays preserve their length, present indexes and holes across initial HTML, client route-data and static output; they are not passed through native JSON array semantics, which would collapse holes to null. Cycles, non-finite numbers, unsupported runtime objects and malformed or unbounded sparse encodings are rejected fail-closed at the owning boundary with a path-attributed error. An own enumerable __proto__ member is also rejected at encode and decode boundaries instead of being handed to userland as a prototype-sensitive record key. Ordinary keys such as constructor and toString remain user data.

Loaders across the layout/page branch run in parallel (one Promise.all per request), not top-down. Do not rely on a parent layout loader having finished before a child loader starts. When a parent resolves to a redirect or notFound(), child loaders have already executed — their side effects are not rolled back, but their results and response headers are discarded from the response.

Across the branch, a redirect() takes precedence over a notFound(), which takes precedence over a thrown error — regardless of layer depth. A child page's redirect() therefore wins even if a parent layout loader threw, because redirect and notFound are deliberate control-flow outcomes while an error is an incidental failure.

For browser builds Energo removes the server loader export and declarations reachable only from it, but it preserves JavaScript module semantics. Ordinary dependencies still evaluate, observable top-level initializers and failures remain, runtime enum initializers execute, and using / await using resources are disposed. Put code that must never enter the client graph behind an explicit .server module boundary; an unmarked dependency is treated as an ordinary ESM module, not silently converted into server-only code.

Dynamic imports in a projected route module must use a string-literal module specifier. Energo rebases and validates that exact client edge; a computed specifier is rejected because its complete target graph cannot be proven free of server-only modules. A literal dynamic import remains lazy and follows the normal Vite client-build semantics.

Static data (SSG)

getServerStaticData runs at build time instead of per request, and getServerStaticPaths enumerates the concrete paths to prerender for dynamic segments:

import type { GetServerStaticData, GetServerStaticPaths } from 'energo/router/types'

export const getServerStaticPaths: GetServerStaticPaths = () => ({
  // Concrete pathnames or params entries — both forms are accepted.
  paths: ['/blog/hello', { params: { slug: 'world' } }],
})

export const getServerStaticData: GetServerStaticData<{ title: string }> = (ctx) => ({
  title: `Post: ${String(ctx.params['slug'])}`,
})

Static loaders see only params, url and signal — there is no request, no cookies and no locals at build time. Enumerated paths are collision-checked at build.

Navigation

Use <Link> for managed client-side navigation with prefetch:

import { Link } from 'energo/link'

;<Link href="/blog" prefetch="visible" activeClass="active">
  Blog
</Link>

prefetch accepts 'hover' (default), 'visible', true (eager) or false. The registration reacts to prop changes: leaving visible mode retires its observer before the next mode takes effect. replace, scroll, activeClass / inactiveClass and end control history, scroll restoration and active-state matching. Managed push/replace navigation scrolls to the target fragment when one is present and otherwise scrolls to the top; scroll={false} preserves the current position. Back/forward traversal restores the position owned by that history entry. Energo only restores history entries that it created: traversing to userland pushState data falls back to a native load rather than treating foreign state as a framework snapshot. Navigation across a locale or root document boundary likewise performs a native load and never replaces the living root layout in place. Managed links follow HTML token semantics: an omitted/empty target and every ASCII-case variant of _self stay in the current document, while other browsing-context targets remain native. rel=external opts out only as an exact ASCII-whitespace-separated, ASCII-case-insensitive token; substrings such as no-external do not disable client navigation or prefetch.

Route-data prefetch reuse honors response Cache-Control, Age and Date using the RFC current-age calculation, including request transit time. Invalid, overflowing or stale freshness metadata and no-cache / no-store are never reused for a later click. A response without explicit freshness or a reuse prohibition can use Energo's five-second same-navigation intent window; this bounded prefetch policy is not an HTTP cache and visible-only warmup is not retained after it settles. The registry holds at most 32 prefetch entries per live document. Adding a newer entry evicts the oldest one; pending network work owned only by an evicted entry is aborted instead of continuing in the background. A newer navigation likewise cancels an older navigation that is waiting on an in-flight prefetch, and the older result can never commit late.

Route data and route-owned assets must become ready before history and UI are committed. A failure before commit falls back to a native load without exposing stale route UI. A failure after commit does not roll back the already committed URL/document state and is reported through the public router error event.

Hooks from energo/router/hooks (all reactive):

| Hook | Returns | | -------------------- | ----------------------------------------------------------------- | | useServerData<T>() | Accessor for the nearest loader's data | | useParams() | Route params record | | useLocation() | { pathname, search, hash, href, searchParams, query, queryAll } | | useSearchParams() | [view, setSearchParams] tuple | | useNavigate() | (href, options?) => Promise<NavigationResult> | | useLocale() | Active locale string | | useRouter() | Full router API |

useParams() and the record view returned by useSearchParams() are stable, readonly, null-prototype live views. Indexed reads, Object.keys, Object.hasOwn, in, property descriptors, spread, and JSON all observe the same current own keys; __proto__, constructor, and toString are ordinary data names. useLocation() applies the same own-property semantics to its fixed field set. For static output, Energo first hydrates against the query-free prerendered snapshot and then reconciles these live views to the actual browser query and fragment.

The setter returned by useSearchParams() patches the current query rather than replacing it wholesale. Supplied scalar or array values replace that key, null and undefined remove that key, and omitted keys remain unchanged. Repeated values retain their supplied order; the current pathname and fragment are preserved.

For browser-only UI (widgets touching window, third-party embeds), wrap it in <ClientOnly> from energo/client-only — the server renders the optional fallback, the client swaps in the real content after hydration.

Head management

<Title>, <Meta> and <LinkTag> from energo/head register head tags from anywhere in the tree. Deeper tags override shallower ones with the same identity (one <title>, <meta name>/property/http-equiv/charset deduplication, component-wise <link> identity):

import { LinkTag, Meta, Title } from 'energo/head'

<Title>Dashboard</Title>
<Meta name="description" content="Team dashboard" />
<LinkTag rel="canonical" href="https://example.com/dashboard" />

Attribute names are allow-listed and values are validated and escaped fail-closed before any HTML is emitted; href accepts relative or absolute http(s) URLs only.

API routes

route.ts exports one handler per HTTP method and always returns a Web Response:

// src/app/routes/api/users/[id]/route.ts
import type { ApiHandlerContext } from 'energo/router/types'

export function GET(ctx: ApiHandlerContext) {
  return Response.json({ id: ctx.params['id'], role: ctx.locals['role'] })
}

export async function POST(ctx: ApiHandlerContext) {
  const body = await ctx.request.json()
  return Response.json({ created: body }, { status: 201 })
}

Supported exports: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS. Unsupported methods get 405 Method Not Allowed with a populated Allow header, HEAD falls back to GET unless defined explicitly, and OPTIONS is synthesized when absent. Request bodies are raw Web Request bodies with Fetch single-consumption semantics; server.maxRequestBodyBytes enforces deterministic 400/413 before your code runs.

Middleware

middleware.ts applies to its branch and everything below. It must return a Response — either its own or the one from next():

// src/app/routes/middleware.ts
import type { Middleware } from 'energo/router/types'

const middleware: Middleware = async (ctx, next) => {
  const session = ctx.cookies.get('session')
  if (session === undefined && ctx.url.pathname.startsWith('/app')) {
    return new Response(null, { status: 302, headers: { Location: '/login' } })
  }
  ctx.locals['userId'] = session
  return await next()
}

export default middleware

ctx.locals is a request-scoped, typed bag shared across middleware, loaders and API handlers. ctx.response.headers merges into the final response. Returning undefined or swallowing next()'s response is a framework error — middleware cannot silently drop a response.

Cookies

energo/http/cookies is a single, strict RFC 6265 cookie API used both by the framework (ctx.cookies) and directly on Web primitives:

import { deleteCookie, getCookie, setCookie } from 'energo/http/cookies'

const theme = getCookie(request, 'theme')
setCookie(headers, 'session', token, {
  httpOnly: true,
  secure: true,
  sameSite: 'lax',
  maxAge: 3600,
  prefix: 'host', // emits __Host-session with enforced invariants
})
deleteCookie(headers, 'session', { prefix: 'host', secure: true })

Invalid names and values are rejected or ignored fail-closed; __Secure- / __Host- prefix invariants, SameSite=None; Secure pairing and the 400-day expiry cap are enforced at the API boundary.

Request parsing uses the strict RFC 6265 cookie-octet grammar. Optional SP/HTAB around pairs and = is treated as bad whitespace, but raw whitespace, quotes, commas, semicolons, backslashes, controls and non-ASCII value bytes are not accepted as cookie data. One complete surrounding quote pair is removed; its contents still follow cookie-octet. Percent escapes are validated as wire data before decoding, so values emitted by serializeCookie() round-trip while malformed percent escapes remain literal. On duplicate names the first valid pair wins; an invalid pair cannot reserve a name.

Configuration

All configuration is optional. Create energo.config.ts next to vite.config.ts when you need it:

import { defineConfig } from 'energo/config'

export default defineConfig({
  server: {
    port: 3000,
    maxRequestBodyBytes: 1024 * 1024,
  },
  i18n: { locales: ['en', 'fr'], defaultLocale: 'en' },
  security: {
    csp: {
      policy: "default-src 'self'; script-src 'self' 'nonce-{{nonce}}'",
      nonce: true,
    },
  },
  build: { output: 'server' },
})

| Section | What it controls | | ---------- | -------------------------------------------------------------------------------------------------------------------------- | | server | port, host, idleTimeout, reusePort, unix, tls (file paths only), maxRequestBodyBytes, onError hook | | i18n | locales + defaultLocale; every locale is served under /<locale>/…, the default locale also owns the unprefixed paths | | security | Opt-in CSP policy with per-request {{nonce}} substitution and reportOnly mode | | build | output ('server' | 'static'), distDir, staticDir, sourcemap, minify, cssMinify, target | | css | CSS Modules class-name strategy | | vite | Guarded, scope-aware escape hatch for shared/dev and client/SSR Vite config |

For energo start and serveBuiltApp(), relative server.unix and server.tls file paths are resolved against the built application root, not the caller's ambient working directory.

Unknown keys and invalid values fail the build with exact, [energo/config]-prefixed messages. Config can also be a function of the build phase (defineConfig((phase, ctx) => ({ … }))). Each command lifecycle evaluates that function once. In particular, energo start uses the same normalized result for CLI environment/default precedence, built-artifact validation and server startup, so side effects and nondeterministic values are never repeated or allowed to diverge. CLI build-output overrides are applied only after the raw config shape has passed validation, so an invalid build value cannot be hidden by --output. The guarded Vite hook validates its permitted alias, define, CSS, PostCSS and dev-proxy value forms before merging them into Vite configuration.

Production Vite config has three explicit hook targets. Put root resolve.alias, CSS and common define values in shared; put target-specific define values in client or ssr. Development uses the single dev target, which also owns server.proxy. Vite aliases and CSS are build-shared by upstream design, so returning either field from client or ssr is a configuration error instead of silently leaking the client value into SSR:

export default defineConfig({
  vite(config, context) {
    if (context.target === 'shared') {
      config.resolve = { alias: { '@app': '/absolute/path/to/src' } }
      return
    }
    if (context.target === 'client' || context.target === 'ssr') {
      config.define = { __BUILD_TARGET__: JSON.stringify(context.target) }
    }
  },
})

Build invocation order is deterministic: shared, client, then ssr. Alias object and array forms use the same replacement semantics, and an exact matcher has one effective rule. The final build table lists every materialized dynamic static path, including required and optional catch-all expansions and their locale-prefixed output paths; slow expansions keep their individual timing diagnostics.

Static export

energo build --output static   # or build.output: 'static' in config

Produces a self-contained out/** tree — HTML, route-data JSON and client assets only — ready for any dumb static host (CDN, nginx, GitHub Pages). The export is validated fail-closed: server-only code, sourcemaps, build internals and framework metadata are proven absent from the final artifact, path collisions fail the build, and a root 404.html is always emitted. Generated route-data files additionally pass the exact canonical framework JSON schema and planned-route identity check after being written to staging; unknown or duplicate framework fields fail the build. Ordinary public/client JSON assets and user strings such as node:fs remain data, not code signals. Publication uses an owned, journaled directory transaction: concurrent export writers conflict explicitly, an interrupted swap is recovered on the next run, and unrelated sibling directories are never removed by name pattern. Symbolic links inside the client/public source trees are rejected instead of being followed into the deploy artifact. The complete application build has a separate success-commit owner: client, SSR, prerender and optional static output are written under one UUID staging session. Vite resolves one shared build config and plugin pipeline for that entire app lifecycle, so config functions execute once and every environment is checked against the owner's exact staging root before writes begin. Exact file inventories are verified, and dist plus out become visible only after every Vite buildApp hook has succeeded. A failed or killed build preserves the previously published trees. Programmatic builds must call buildEnergoApp({ plugins: [energo()] }); raw Vite build()/builder.buildApp() are rejected because they cannot own this publication boundary. Client assets are provenance-checked across the Vite main graph, production workers, CSS/import-meta URL transforms and publicDir: only bundle-declared or validated public files with unchanged byte identities may reach server/static output. The build serializes that exact versioned path/source/ SHA-256 inventory as dist/.energo-client-artifacts.json; energo start revalidates the complete client tree once at startup and builds its static route map from the verified inventory, including copied publicDir files. Static export consumes the same verified dist/client snapshot and never re-reads live publicDir. Public filesystem names are encoded per URL segment, so spaces, Unicode, percent signs, route-syntax characters and .well-known/** remain addressable without becoming Bun route patterns. _energo/**, public sourcemaps, unknown files and byte drift fail closed. No inventory scan or hash work occurs on the request hot path. The .server marker applies to every asset extension and Vite query channel, not only TypeScript/JavaScript modules. Runtime-only features (CSP nonces, middleware, API routes) reject static output at build time instead of silently degrading.

Framework-owned build validation failures carry a stable owner prefix so a caller can distinguish the rejected contract boundary from a fixture syntax, dependency or toolchain failure. Public owner categories are [energo/codegen], [energo/vite], [energo/static-export], [energo/config], [energo/build], [energo/build-output] and [energo/client-artifacts]; explanatory prose after the prefix is diagnostic, not a string-matching API. Configuration owns invalid build-path values; [energo/build] owns the outer multi-target publication transaction.

CLI

energo dev      Start the Vite development server (HMR + SSR middleware).
energo build    Build client + SSR + SSG in a single pass.
energo start    Run the production server (reads dist/, loads energo.config.ts).

  --host [host]     Bind host for dev/start (no value = 0.0.0.0).
  --port <port>     Bind port for dev/start.
  --output <mode>   Build output mode: server | static.

There is intentionally no energo preview: vite preview would serve only static client files and silently bypass SSR, middleware and API routes — use energo build && energo start instead.

Module map

Every public export lives on an explicit subpath — there is no root import:

| Subpath | Exports | | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- | | energo/vite | energo() Vite plugin, buildEnergoApp() canonical programmatic build | | energo/config | defineConfig, config types | | energo/head | Title, Meta, LinkTag | | energo/link | Link | | energo/client-only | ClientOnly | | energo/router/hooks | useServerData, useParams, useLocation, useSearchParams, useNavigate, useLocale, useRouter | | energo/router/redirect | redirect() | | energo/router/not-found | notFound() | | energo/router/types | GetServerData, Middleware, ApiHandlerContext, … (types only) | | energo/http/cookies | getCookie, getCookies, parseCookie, serializeCookie, setCookie, deleteCookie | | energo/client | Client hydration entry (used by generated code) | | energo/server/serve | createServer, createRequestHandler, serveBuiltApp — programmatic server embedding | | energo/server/config, energo/server/manifest, energo/server/types, energo/render/* | Advanced server/render integration surfaces |

Deeper design and security documentation (docs/architecture.md, docs/security.md) lives in the repository.

License

MIT