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

@finesoft/front

v0.1.59

Published

Full-stack framework: router, DI, actions, SSR, and server — all in one package

Readme

@finesoft/front

Full-stack TypeScript framework — router, DI, actions, SSR, and server — all in one package.

Works with Vue, React, and Svelte. Deploy to Node.js, Vercel, Cloudflare Workers, Netlify, or static hosting.

Install

npx create-finesoft-app my-app

Or add to an existing project:

npm install @finesoft/front

Peer dependencies: hono >= 4.0.0. Optional: @hono/node-server, vite >= 5.0.0.

Setup

// vite.config.ts
import { finesoftFrontViteConfig } from "@finesoft/front";
import vue from "@vitejs/plugin-vue";
import { defineConfig } from "vite";

export default defineConfig({
    plugins: [
        vue(),
        finesoftFrontViteConfig({
            ssr: { entry: "src/ssr.ts" },
            i18n: { messagesDir: "src/locales" },
            proxies: [{ prefix: "/api", target: "https://api.example.com" }],
            adapter: "auto",
        }),
    ],
});

Routing

// src/bootstrap.ts
import { type Framework, defineRoutes } from "@finesoft/front";
import { HomeController } from "./lib/controllers/home";
import { authGuard } from "./lib/guards/auth";

export function bootstrap(framework: Framework): void {
    defineRoutes(framework, [
        { path: "/", intentId: "home", controller: new HomeController() },
        {
            path: "/about",
            intentId: "about",
            controller: new AboutController(),
            renderMode: "csr",
        },
        { path: "/admin", intentId: "home", beforeLoad: [authGuard] },
    ]);
}

Controllers

import { BaseController, type Container } from "@finesoft/front";

class HomeController extends BaseController<Record<string, string>, HomePage> {
    readonly intentId = "home";

    async execute(_params: Record<string, string>, container: Container) {
        const http = container.resolve<HttpClient>("http");
        return http.get("/api/home");
    }
}

Middleware

Two-phase guards shared between SSR and CSR:

import { next, redirect, type NavigationContext } from "@finesoft/front";

function authGuard(ctx: NavigationContext) {
    return ctx.getCookie("token") ? next() : redirect("/login");
}

Results: next(), redirect(url, status?), rewrite(url), deny(status?, message?).

Lifecycle Hooks

startBrowserApp provides hooks for initialization:

import { startBrowserApp } from "@finesoft/front/browser";

startBrowserApp({
    bootstrap,
    // Pass framework config so locale/reporting/etc. are wired into Framework
    frameworkConfig: {
        locale: "zh-Hans",
    },
    mount: (target, { framework }) => {
        /* ... */
    },
    callbacks: { onNavigate, onExternalUrl },
    onBeforeStart(framework) {
        // Runs after Framework creation, before mount.
        // Good for: error monitoring, analytics SDK, i18n init.
    },
    onAfterStart(framework) {
        // Runs after initial page trigger.
        // Good for: service worker registration, performance marks.
    },
});

Locale (i18n)

Pass locale in FrameworkConfig to enable automatic locale handling:

const framework = Framework.create({
    locale: "zh-Hans", // or "en-US", "ar-SA", etc.
});

// SSR: automatically injects <html lang="zh-Hans" dir="ltr">
// Browser: automatically sets document.documentElement.lang/dir on startup

For SSR, locale is resolved in this order:

  1. Explicit resolveLocale callback (if provided)
  2. locale from FrameworkConfig (via DI container)

Access at runtime:

const locale = framework.getLocale();
// { lang: "zh-Hans", dir: "ltr" }

Translator

For synchronous in-memory i18n translation, use SimpleTranslator:

import { SimpleTranslator } from "@finesoft/front";

const t = new SimpleTranslator({
    locale: "zh-Hans",
    messages: {
        hello: "你好",
        "items.one": "{count} 个项目",
        "items.other": "{count} 个项目",
    },
});

t.t("hello"); // "你好"
t.t("hello", { name: "World" }); // "你好"
t.plural("items", 5); // "5 个项目"

For locale dictionaries, prefer JSON files plus finesoftFrontViteConfig(). The framework will automatically load ${locale}.json before SSR render and before browser hydration, without serializing the translations into HTML.

// vite.config.ts
import { finesoftFrontViteConfig } from "@finesoft/front";
import { defineConfig } from "vite-plus";

export default defineConfig({
    plugins: [
        finesoftFrontViteConfig({
            ssr: { entry: "src/ssr.ts" },
            i18n: {
                messagesDir: "src/locales",
            },
        }),
    ],
});

// src/locales/zh-Hans.json
{
    "hello": "你好"
}

// src/locales/en-US.json
{
    "hello": "Hello"
}

If you need a non-file source such as a CDN or API, loadMessages is still supported on createSSRRender() / startBrowserApp() and overrides the Vite-generated loader.

RTL Support

import { isRtl, getTextDirection, getLocaleAttributes } from "@finesoft/front";

isRtl("ar"); // true
getTextDirection("he"); // "rtl"
getLocaleAttributes("ar-SA"); // { lang: "ar-SA", dir: "rtl" }

Metrics & Event Recording

EventRecorder (recommended)

New pipeline for structured event recording. Default: ConsoleEventRecorder (logs to console).

import { Framework, type EventRecorder } from "@finesoft/front";

// Custom recorder
const framework = Framework.create({
    eventRecorder: myAnalyticsRecorder,
});

// Framework automatically records PageView events via didEnterPage()

Compose multiple recorders:

import { CompositeEventRecorder, ConsoleEventRecorder } from "@finesoft/front";

const recorder = new CompositeEventRecorder([new ConsoleEventRecorder(), myProductionRecorder]);

Inject common fields into every event:

import { WithFieldsRecorder } from "@finesoft/front";

const recorder = new WithFieldsRecorder(baseRecorder, [
    { getFields: () => ({ app: "myApp", version: "1.0" }) },
]);

Impression Tracking

Track element visibility using IntersectionObserver:

import { IntersectionImpressionObserver } from "@finesoft/front";

const observer = new IntersectionImpressionObserver((entries) => {
    for (const entry of entries) {
        analytics.track("impression", { id: entry.id, ...entry.metadata });
    }
});

observer.observe(element, "product-card-123", { category: "featured" });
// Later:
observer.unobserve(element);
observer.destroy();

Error Reporting

Send warn/error logs to an external monitoring service (Sentry, Datadog, etc.):

import { Framework, type ReportCallback } from "@finesoft/front";

const framework = Framework.create({
    reportCallback(level, category, args) {
        sentry.captureMessage(`[${category}] ${args.join(" ")}`, level);
    },
});

// Automatically composes with ConsoleLogger — console output is preserved.
// All framework.getLogger().warn(...) and .error(...) calls are forwarded.

HTTP Client

Subclass HttpClient to create typed API clients:

import { HttpClient } from "@finesoft/front";

class MyApi extends HttpClient {
    async getUser(id: string) {
        return this.get<User>(`/users/${id}`);
    }
    async createUser(data: NewUser) {
        return this.post<User>("/users", data);
    }
}

Interceptors

Add request/response interceptors for auth, logging, retries, etc.:

const api = new MyApi({
    baseUrl: "/api",
    requestInterceptors: [
        (url, init) => {
            init.headers = {
                ...init.headers,
                Authorization: `Bearer ${token}`,
            };
            return init;
        },
    ],
    responseInterceptors: [
        (response, url) => {
            if (response.status === 401) refreshToken();
            return response;
        },
    ],
});

// Or add dynamically:
api.useRequestInterceptor((url, init) => {
    /* ... */ return init;
});

Platform Detection

Automatically detected from User-Agent and available via DI:

const platform = framework.getPlatform();
// { os: "ios", browser: "safari", engine: "webkit", isMobile: true, isTouch: true }

Standalone usage:

import { detectPlatform } from "@finesoft/front";
const info = detectPlatform(); // auto-reads navigator.userAgent

PWA Detection

import { getPWADisplayMode } from "@finesoft/front";

const mode = getPWADisplayMode();
// "standalone" | "twa" | "browser"

Feature Flags

const framework = Framework.create({
    featureFlags: { darkMode: true, maxRetries: 3 },
});

// Add remote providers (last registered wins):
import { type FeatureFlagsProvider } from "@finesoft/front";

const framework = Framework.create({
    featureFlags: { darkMode: false },
    featureFlagsProviders: [remoteConfigProvider],
});

DI Container

Scoped containers for request isolation (SSR):

const requestScope = framework.container.createScope();
requestScope.register("user", () => currentUser);
// Falls back to parent container if key not found

SSR

// src/ssr.ts
import { createSSRRender, serializeServerData } from "@finesoft/front";
import { createSSRApp } from "vue";
import { renderToString } from "vue/server-renderer";
import App from "./App.vue";
import { bootstrap } from "./bootstrap";

export const render = createSSRRender({
    bootstrap,
    getErrorPage: () => ({ title: "Error", kind: "error" }),
    async renderApp(page) {
        const html = await renderToString(createSSRApp(App, { page }));
        return { html, head: `<title>${page.title}</title>`, css: "" };
    },
});

export { serializeServerData };

Adapters

| Adapter | Target | | -------------- | -------------------------- | | "node" | Standalone Node.js server | | "vercel" | Vercel Build Output API v3 | | "cloudflare" | Cloudflare Workers | | "netlify" | Netlify Functions v2 | | "static" | Pre-rendered static files | | "auto" | Auto-detect at build time |

Entry Points

| Import | Contents | | ------------------------- | ------------------------------------------ | | @finesoft/front | Everything (core + browser + SSR + server) | | @finesoft/front/browser | Browser-only (no server code) |

License

MIT