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

@noego/forge

v2.6.0

Published

Portable Svelte 5 frontend executor for NoEgo version 1 applications. Forge matches an executable frontend route, runs its middleware, controller adapter, and loaders, renders the view through its layout chain during SSR, and returns a Fetch API `Response

Readme

@noego/forge

Portable Svelte 5 frontend executor for NoEgo version 1 applications. Forge matches an executable frontend route, runs its middleware, controller adapter, and loaders, renders the view through its layout chain during SSR, and returns a Fetch API Response.

@noego/app normally compiles the executable binding and generates the browser hydration entry. Use Forge directly when implementing another build tool or runtime host.

Installation

npm install @noego/forge svelte

Svelte ^5.28.2 is a peer dependency.

Imports

import {
  createFrontendApplication,
  renderFrontendRoute,
  matchFrontendRoute,
  type ExecutableFrontendBindingV1,
} from '@noego/forge';

The root export and @noego/forge/v1 are equivalent. Import the layout engine component from @noego/forge/v1/RecursiveRender.svelte.

Create an application

createFrontendApplication converts an executable binding into the portable NoEgo application shape:

(request: Request, context: NoegoRuntimeContext) => Promise<Response>
import {
  createFrontendApplication,
  type ExecutableFrontendBindingV1,
} from '@noego/forge';

const binding: ExecutableFrontendBindingV1 = {
  routes: [
    {
      path: '/users/{id}',
      method: 'get',
      view: UserView,
      layouts: [AppLayout],
      controller: null,
      middleware: [],
      // One loader for each layout, followed by the view loader.
      loaders: [
        async () => ({ navigation: [] }),
        async ({ params }) => ({ userId: params.id }),
      ],
    },
  ],
  fallback: {
    view: NotFoundView,
    layouts: [AppLayout],
    middleware: [],
  },
  middleware: {},
};

const application = createFrontendApplication(binding);
const response = await application(request, runtimeContext);

Unmatched routes return the configured fallback with status 404. Without a matching route or fallback, the application returns a plain 404 response.

Executable binding

A frontend route contains:

  • path and HTTP method
  • a Svelte view
  • outer-to-inner layouts
  • an optional executable controller
  • middleware identifiers
  • one loader per layout plus one loader for the view

The loader count must equal layouts.length + 1. Forge runs the loaders concurrently after the controller adapter. Each loader receives:

interface FrontendExecutionInput {
  request: Request;
  context: NoegoRuntimeContext;
  params: Record<string, string>;
  query: URLSearchParams;
  controller?: unknown;
}

A loader returns the props for its corresponding layout or view:

export default async function load({ params }: FrontendExecutionInput) {
  return { userId: params.id };
}

The executable controller is a function that receives the same input and returns controllerProps. When using @noego/app, the build tool creates this adapter from the route's page-controller module.

Page controllers with @noego/app

The public PageController<Data, Input> interface describes the browser controller shape produced by App:

interface PageController<Data, Input> {
  data: Data;
  input: Input;
  initialize?(loadData: Record<string, unknown>): void | Promise<void>;
  destroy?(): void;
}

A route may reference a *.svelte.ts controller class. App constructs the class for SSR and browser hydration, passes data and input to the view, calls initialize(loadData) after hydration, and calls destroy() during cleanup.

Middleware

Bindings map route middleware identifiers to portable functions:

const middleware = {
  session: async (input, next) => {
    const response = await next();
    response.headers.set('x-rendered-by', 'forge');
    return response;
  },
};

Middleware receives the frontend execution input and a next() function. The current application executor requires the middleware chain to produce a Response and rejects a middleware implementation that does not.

Rendering and the HTML shell

renderFrontendRoute:

  1. runs the controller adapter;
  2. runs the layout and view loaders;
  3. renders RecursiveRender with Svelte's server renderer;
  4. serializes loader data into window.__INITIAL_DATA__;
  5. inserts the result into the binding's HTML shell.

Without a custom shell, Forge uses assembleHtmlShell. A binding may provide:

shell({ head, css, app, data }) {
  return `<!doctype html>
<html>
<head>${head}<style>${css}</style></head>
<body>
  <div id="app">${app}</div>
  <script>window.__INITIAL_DATA__=${data}</script>
  <script type="module" src="/app.js"></script>
</body>
</html>`;
}

Browser hydration with @noego/app

App generates the noego:frontend virtual module from the frontend route document. The browser entry should mount Forge's route tree over the SSR markup:

import { mountFrontend } from 'noego:frontend';

mountFrontend();

The generated module reuses window.__INITIAL_DATA__ and hydrates RecursiveRender; it falls back to a clean mount when hydration is not possible.

JSON data requests (client-side navigation)

A request to a page route that prefers JSON gets the route's loader data only — the exact object the HTML shell would embed as window.__INITIAL_DATA__ — instead of a rendered page:

GET /users/42
Accept: application/json

200 content-type: application/json
{ "layout": [...], "view": {...} }

Rules:

  • The Accept header must prefer application/json over text/html (wantsLoaderData(request) is exported). Browser-style headers such as text/html,...,application/json;q=0.8 keep the HTML render.
  • Middleware runs first either way — a middleware redirect (302) applies to data requests exactly as it does to page requests.
  • An unmatched path answers through the fallback route with 404 JSON.

This is the server half of client-side navigation: @noego/app's generated noego:frontend module fetches page URLs this way on intercepted link clicks and remounts the route with the returned data.

Client navigation: scroll and prefetch

Forge owns the client-side navigation behavior contract (client-navigation.ts, consumed by @noego/app's generated runtime).

Scroll. A client-side navigation lands like a full page load: the viewport scrolls to the top — or to the URL's #fragment element when one exists. A link opts out with scroll="static", which swaps the page and leaves the scroll position alone (useful for tabs, filters, and other in-place page changes):

<a href="/settings/profile" scroll="static">Profile</a>

Back/forward navigations never force a scroll — the browser restores the position itself.

Prefetch. A link may declare a prefetch trigger; the moment the trigger fires, the destination's loader JSON (the same payload a click would fetch) is requested ahead of time, so the navigation lands on warm data:

<a href="/projects" prefetch="hover">Projects</a>   <!-- on hover or focus -->
<a href="/projects" prefetch="click">Projects</a>   <!-- on pointer-down -->
  • on: prefixes are accepted: prefetch="on:click" ≡ prefetch="click".
  • On touch devices touchstart warms both kinds (there is no hover).
  • Prefetches are single-flight per URL, expire after 10 seconds, and are consumed by the next navigation to that URL; a failed prefetch is discarded so the real navigation retries from scratch.
  • Only same-origin URLs that match a configured page route are prefetched.

The primitives (resolveScrollBehavior, applyNavigationScroll, normalizePrefetchTrigger, createPrefetchCache, installPrefetchListeners) are exported from @noego/forge/v1 for custom runtimes.

Loaders and page controllers must not self-fetch during SSR

A loader or page controller executes inside the request currently rendering the page. It must not call the application's own HTTP surface with a relative URL or the rendering request's origin:

// Do not do this during SSR.
await fetch('/api/users');
await fetch(new URL('/api/users', input.request.url));

Resolve the service or repository through the application's IoC container instead. External hosts are allowed, and browser event handlers may call the application API normally.

renderFrontendRoute installs this guard automatically. LoaderSelfFetchError has code forge/self-fetch. installSelfFetchGuard and withSelfFetchGuard are exported for runtime hosts and tests.

Public API

The package exports:

  • createFrontendApplication
  • matchFrontendRoute
  • renderFrontendRoute
  • wantsLoaderData
  • assembleHtmlShell
  • RecursiveRender through its Svelte subpath export
  • LoaderSelfFetchError, installSelfFetchGuard, and withSelfFetchGuard
  • frontend binding, route, middleware, runtime-context, controller, and shell types

Node frontend application surface (@noego/forge/testing/frontend)

The fast frontend application slice (spec 18/18A): open a production page by its operationId-based identity, drive the PageController's public data/input/events contract, cross the real in-process backend through an injected transport, and observe production navigation — no DOM, no listener, no global patching. This subpath is Svelte-free and safe to import in plain Node.

import { createFrontendSurface, trackTransport, FrontendNavigator } from '@noego/forge/testing/frontend';

const { frontend, navigator } = createFrontendSurface({ routes, root, pending: [inFlight] });
const page = await frontend.open({ page: 'connect.login' });
await frontend.act(() => page.input.submit());
frontend.current();          // the page production navigation landed on
await frontend.settle();     // fixed-point drain of owned work
frontend.errors;             // unhandled loader/dispose/navigation failures

Semantics (Phase 0 decisions D-FAS-01/02/04/05):

  • open({ page }) is required and validates the identity against the selected aperture; short names resolve only when unique, ambiguity lists qualified <module>.<pageId> candidates.
  • Navigation targets are production URLs (what navigate('/path') emits) or page identities, resolved through the same catalog; a destination outside the aperture fails with a focused diagnostic instead of widening the test.
  • act()/settle() drain framework-owned work only: tracked transport requests (trackTransport) and navigation transitions. Timer-based async is outside the guarantee and fails loudly (SettleNonConvergenceError).
  • FrontendPage exposes identity, live data/input/events references, params, and close() — never the controller instance.
  • Plain Node proves explicit controller behavior and $state/$derived reachable through it; $effect/component-root behavior belongs to the happy-dom and browser tiers.

Product entry: testApp(config).select({ client: { module | page | pages } }) .buildFrontend() in @noego/app/testing wires this surface up from the production config — auto-imported controllers/loaders, a separate client IoC root, the in-process Dinner transport with a browser-parity cookie jar, and env.fetch/env.navigator for page dependencies.