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

scale-framework

v0.1.2

Published

SSR-first full-stack framework starter for the Scale language

Readme

Scale Framework (SSR-first)

Scale is an SSR-first full-stack framework prototype with:

  • File-system routing from app/**/index.scale
  • Parser-based .scale templates (AST, no regex rendering)
  • Server functions in server.js with func:Name bindings
  • Server-driven component rendering from components/*.scale
  • Secure event flow (session token + CSRF + server-owned function map/state)

Quick Start

npm install
npm run dev

Open http://localhost:3000.

CLI

The package now ships first-party CLI commands:

create-scale-app my-app
cd my-app
npm install
npm run dev

By default this uses a local file: dependency pointing to the framework source (good for local development).
Use --registry if you want package.json to use a semver dependency instead.

create-scale-app my-app --registry

Generate routes/components inside an existing app:

scale generate route /weather/[city]
scale generate component weather-item

Routing

Static routes

  • app/index.scale -> /
  • app/about/index.scale -> /about

Dynamic routes

  • app/city/[city]/index.scale -> /city/seattle
  • Catch-all supported with [...parts]

Route params are injected into state as both:

  • params object ({{params.city}})
  • top-level fields ({{city}})

Layouts

Use layout.scale in app/ or nested route folders.

  • Parent and child layouts compose automatically.
  • Layouts can render <slot />, <slot></slot>, or {{children}}.

Error pages

Optional:

  • app/404.scale for unmatched routes
  • app/500.scale for server errors

Middleware

Optional middleware files:

  • app/middleware.js (global)
  • app/<route>/middleware.js (route-specific)

Middleware can export either:

  • module.exports = async (ctx) => { ... }
  • module.exports = { onRequest: async (ctx) => { ... } }

ctx.phase is "load" or "event". Return { state } to merge state, { redirect, statusCode } to redirect, or { statusCode, body } to block.

.scale Syntax

<div func:Waitlist>
  <button func:SetCounter var:{type: "plus"}>Add</button>
  <p>{{number}}</p>
</div>

Features:

  • func:Name binds an element to a server function.
  • Interactive func defaults to click on non-form elements and submit on <form> elements; onload-only funcs do not bind click handlers.
  • var:{...} passes static element metadata to handlers as self.
  • attrs:path.to.attrs applies server-defined HTML attributes from state.
  • {{key}} and {{nested.key}} interpolate server state.
  • {{static:key}} reads from an immutable snapshot captured after route onload.
  • {{comp.slot}} can render component descriptors.
  • {{comp.list}} can render arrays of component descriptors.
  • Interpolations inside <script> tags render raw text (no HTML escaping or <span> wrappers).

Example:

<a attrs:ui.cityLink>{{city}}</a>
server.return({
  ui: {
    cityLink: {
      href: "/weather/new-york",
      title: "Open New York",
      "data-temp": 72,
    },
  },
});

Components

components/form.scale becomes components.form() in handlers.

components/weather-item.scale becomes components["weather-item"]().

External component libraries (installable)

You can install a component package and register it in createScaleServer:

const path = require("node:path");
const { createScaleServer } = require("scale-framework");

const server = createScaleServer({
  appDir: path.join(__dirname, "app"),
  componentsDir: path.join(__dirname, "components"),
  componentSources: [
    { package: "@acme/scale-ui", prefix: "ui" },
  ],
});

Then call package components via the prefix:

components["ui/button"]({ label: "Save" });

Notes:

  • componentSources entries can be string paths or objects.
  • { package: "name" } resolves from installed node_modules.
  • Optional package metadata: package.json -> scale.componentsDir (defaults to "components").
  • Local componentsDir is still loaded automatically and can override package components with the same names.

Returning one component

server.return({
  comp: {
    menu: components.form(),
  },
});

Returning an array of components

server.return({
  comp: {
    weather: cities.map((city) => components["weather-item"](city)),
  },
});

Server Handler API

In server.js, each exported function gets:

  • element: event registration
  • server: response helpers
  • components: component factories
  • data: form/data validation helpers
const FormSubmit = ({ element, server, components, data }) => {
  element.onsubmit((props) => {
    const validation = data.validate(props, {
      name: { type: "string", required: true, minLength: 2 },
      email: { type: "string", required: true, email: true },
    });

    if (!validation.ok) {
      server.validation(validation.errors, {
        ...props,
        comp: { menu: components.form({ error: "Fix the fields" }) },
      });
      return;
    }

    server.return({
      ...props,
      ...validation.values,
      comp: {
        menu: components.thanks(validation.values),
      },
    });
  });
};

element methods

  • element.onload(handler, options?)
  • element.onclick(handler)
  • element.onsubmit(handler)
  • element.on("event", handler, options?)

Handler signature is (props, self).

Deferred load is supported:

element.onload(async (props) => {
  const data = await fetchSomething();
  server.return({ ...props, data });
}, { defer: true });

With defer: true, the initial page HTML is sent first, then the deferred load patch is applied once the handler finishes.

TypeScript/Typed JS support

Type definitions are bundled for handler context and server APIs (element, server, components, data).

For typed handler exports, use:

const { defineHandlers } = require("scale-framework");

module.exports = defineHandlers({
  HomePage: ({ element, server }) => {
    element.onload((props = {}) => {
      server.return(props);
    });
  },
});

server methods

  • server.return(statePatch)
  • server.update(props, statePatch) merges previous props + statePatch and merges comp maps automatically
  • server.component(name, props)
  • server.redirect(location, statusCode = 303)
  • server.validation(errors, statePatch)
  • server.invalid(errors, props, statePatch) same as validation, but with automatic props + comp merge

Forms/Data Layer

Built-in data helpers (data):

  • data.asString(source, key, options)
  • data.asNumber(source, key, options)
  • data.asBoolean(source, key, options)
  • data.pick(source, keys)
  • data.requireFields(source, keys)
  • data.validate(source, schema)

Form submit events send form fields automatically to the server.

Security Model

State is intentionally ephemeral/in-memory (not permanent), but the event pipeline is server-authoritative:

  • Client sends only sessionId, csrfToken, elementId, event, form.
  • Server resolves func + self from the rendered element map in session state.
  • Client cannot choose arbitrary server function names.

Implemented protections:

  • CSRF token validation per session
  • Same-origin Origin validation for event POSTs
  • Event rate limiting
  • JSON payload size limits
  • Security headers (CSP, X-Frame-Options, nosniff, etc.)

Rendering Model

  • SSR first render on GET.
  • onload handlers produce initial state.
  • onload(..., { defer: true }) runs after first paint and patches the page when finished.
  • Events (click, submit, custom) call server handlers.
  • Server re-renders route view and returns HTML fragment.
  • Client swaps [data-scale-root] content with the new server-rendered view.

Dev/Build Pipeline

npm run dev      # Node watch mode
npm run test     # Node test runner
npm run build    # outputs .scale-build/
npm run preview  # run built output

Dev Error Overlay + Template Mapping

In dev mode (NODE_ENV !== "production"), server/template failures return structured errors and the browser shows an overlay with:

  • message and stack
  • .scale template file path
  • mapped line/column
  • source frame snippet

Template/Component Live Reload

Dev mode exposes /_scale/hmr (SSE). The client runtime listens for .scale / route server.js changes and performs a live page reload.

Build output includes a route manifest: .scale-build/route-manifest.json.

VS Code Language Support + Linter

A local extension is included at:

  • vscode/scale-language

It provides:

  • .scale syntax highlighting
  • snippets
  • go to definition from func:Name to sibling server.js exports
  • inline diagnostics for tags/interpolations/directives
  • func: to server.js export checks (when server.js is in the same folder)

Run extension tests:

node --test vscode/scale-language/test/*.test.js

Use in VS Code:

  1. Open vscode/scale-language as a project in VS Code.
  2. Press F5 to launch an Extension Development Host.
  3. Open any .scale file in the dev host.

Current Example Routes

  • / waitlist form + validation + component swap
  • /weather array-mapped weather components
  • /city/seattle dynamic route param demo

Notes

  • Session/state is in-memory by design (ephemeral, not persistent storage).
  • This is still a framework prototype; production hardening and ecosystem tooling can be expanded further.