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

@suluk/builder

v0.1.16

Published

Tiered contract-narrowing DSL (components→blocks→sections→pages) bound to the Suluk cycle. A page composes sections; buildApp emits backend (routes+v4) AND frontend (page TSX) at once. Inspired by multivendorbuilder's DSL, rebuilt with the Suluk disciplin

Readme


CANDIDATE tooling — not official OpenAPI. Suluk is a single-contributor candidate for OpenAPI Specification v4.0 ("Moonwalk"), unaffiliated with the OpenAPI Initiative and unable to ratify anything on the SIG's behalf.

Install

bun add @suluk/builder

What it does

  • One spec → both ends. Give buildApp a list of entities (each a v4 Schema Object); it returns the backend (5 CRUD RouteContracts per entity, emitted to a valid v4 document) and the frontend (a shadcn Form + Table per entity, plus a page TSX that composes them).
  • A tiered contract-narrowing DSLcomponents → blocks → sections → pages. Each tier hardcodes most of the tier below and re-publishes a deliberately narrower params. A page can reorder/hide a section's blocks and set its tone, but it cannot reach into the form's fields — those simply aren't in the section's contract, so the validator rejects them. The narrowing is the safety surface.
  • Modules — a SulukModule is a mergeable v4 contract fragment (entities + CRUD + cost + provider slots). installModule merges one into the app document and refuses on any collision or unmet requirement (never partially applies). composeModules installs a whole set in dependency order.
  • DistributiontoShadcnRegistry packages each entity slice (its components + backend routes + v4 schema) as one installable shadcn registry item; signRegistry/verifyRegistrySignature add publisher provenance for an open marketplace.

Everything is pure and synchronous (no I/O, no runtime), so the whole composition is unit-tested.

When to reach for it

  • You want to compose a whole app declaratively from first-party building blocks rather than hand-wiring routes, forms, and tables for every entity.
  • You're building a module/registry surface — installable, composable contract fragments with collision-refusal, dependency ordering, swappable provider slots, and signed provenance.
  • You're authoring a landing page as a projection (hero/features/pricing/testimonials/faq/cta/footer) with i18n-key copy + SEO meta + JSON-LD — see buildMarketing.

Reach past it for the pieces it composes: the underlying schema→contract conversion is @suluk/zod / @suluk/drizzle, the route → v4 + validation engine is @suluk/hono, and the form/table TSX is @suluk/shadcn. @suluk/builder orchestrates those; it doesn't replace them.

Usage

Build a whole app from entities (the common case)

import * as z from "zod";
import { zodToV4 } from "@suluk/zod";
import { buildApp, type Entity } from "@suluk/builder";

// entities as v4 Schema Objects (Zod → v4 is the cycle's standard path)
const Pet: Entity = {
  name: "Pet",
  schema: zodToV4(z.object({
    id: z.number().int().optional(),
    name: z.string().min(1),
    status: z.enum(["available", "sold"]),
  })).schema,
};
const Category: Entity = {
  name: "Category",
  schema: zodToV4(z.object({ id: z.number().int().optional(), name: z.string() })).schema,
};

const app = buildApp({ entities: [Pet, Category], info: { title: "Shop", version: "1.0.0" } });

app.backend.routes;          // 10 RouteContracts (5 CRUD × 2 entities): listPet, createPet, …
app.backend.document;        // a valid OpenAPI v4 document emitted from those routes
app.frontend.components;     // [{ name: "PetForm", tsx }, { name: "PetTable", tsx }, …]
app.frontend.pages;          // [{ name: "App", tsx }] — page composing every entity's CRUD section
app.errors;                  // DslError[] — empty ⇒ the composition is contract-sound

crudRoutesFromSchema(name, schema) is the unit behind the backend if you only want the routes for one entity.

Package each slice as an installable shadcn registry

import { buildApp, toShadcnRegistry } from "@suluk/builder";

const reg = toShadcnRegistry(app, { name: "pets-registry" });

reg.index;   // registry.json — name + homepage + items[]
reg.items;   // one registry:block per entity (Form + Table + backend routes + v4 schema)
             // plus one registry:page per generated page
// each block installs the whole vertical via `npx shadcn add pet-crud`

Compose a platform from modules

A SulukModule is a mergeable v4 contract fragment. The package ships a curated first-party registry (AUTH, ECOMMERCE, CRM, BILLING, MARKETING) and composeModules installs a set in dependency order (providers before requirers) through the same refuse-on-collision gate:

import { composeModules, AUTH, ECOMMERCE, CRM } from "@suluk/builder";

const empty = { openapi: "4.0.0-candidate", info: { title: "App", version: "1.0.0" }, paths: {}, components: { schemas: {} } };

// deliberately out of order — planComposition topologically sorts so AUTH (provides User) installs first
const { doc, steps, ok } = composeModules(empty, [ECOMMERCE, CRM, AUTH]);
ok;                                            // true ⇒ every requirement met, no collision, all installed
Object.keys(doc.components!.schemas!);         // Account, Cart, Contact, Deal, Order, Product, Session, User, …

Install one module directly (and preview before committing):

import { installModule, previewInstall, namespaceModule } from "@suluk/builder";

const preview = previewInstall(appDoc, ECOMMERCE);
preview.willInstall;    // boolean
preview.conflicts;      // why it would be refused (collisions / unmet requires)
preview.addsSchemas;    // ["Product", "Order", …]
preview.grade;          // { grade: "A" | "B" | "C", costCoverage, warnings, … }

const result = installModule(appDoc, ECOMMERCE);
if (!result.installed) {
  // refused — appDoc is UNCHANGED; result.conflicts explains why
  // resolve a name clash by namespacing the module, then retry:
  installModule(appDoc, namespaceModule(ECOMMERCE, "Shop"));  // ⇒ ShopProduct, ShopOrder, …
}

Build a stack from a named template:

import { resolveTemplate, STACK_TEMPLATES, composeModules } from "@suluk/builder";

const saas = STACK_TEMPLATES.find((t) => t.name === "SaaS starter")!;   // auth + billing + crm
const { modules, missing } = resolveTemplate(saas);                     // missing reports any unresolved name
const platform = composeModules(emptyDoc, modules);

Swap a provider slot

A module declares providerSlots (e.g. { payments: "stripe" }), recorded into the document as x-suluk-providers. Swap one binding for another implementation of the same interface:

import { readProviders, swapProvider, PROVIDER_CATALOG } from "@suluk/builder";

readProviders(doc);                          // [{ facet: "payments", impl: "stripe", alternatives: […] }]
const { doc: next, error } = swapProvider(doc, "payments", "paddle");   // error set (doc unchanged) on a bad swap
PROVIDER_CATALOG.payments;                   // stripe, paddle, lemonsqueezy (all the same PaymentProvider interface)

Build a marketing landing (landing-as-projection)

import { buildMarketing, seoMeta, jsonLd, type MarketingSpec } from "@suluk/builder";

const spec: MarketingSpec = {
  hero: { titleKey: "home.heroTitle", ctaKey: "home.getStarted", ctaHref: "/signup" },
  features: { featureKeys: ["home.f1", "home.f2"] },
  pricing: { plans: [{ id: "pro", nameKey: "plans.pro", priceCents: 2900, featureKeys: ["plans.f1"], ctaKey: "plans.cta", stripePriceId: "price_123" }] },
  testimonials: {},   // present ⇒ include; default source = approved Reviews (the app resolves the query)
  faq: {},
};

const landing = buildMarketing(spec);     // { sections, blocks, page, errors } — synchronous, no fetch
const meta = seoMeta({ title: "Acme", description: "The app." });        // og* default from title/description
const ld = jsonLd("Organization", { name: "Acme", url: "https://acme.test" });   // schema.org for <head>

Copy is carried as i18n message keys (the app resolves them per locale) and data-driven sections carry a declarative source projection — never literal copy or an author-time fetch.

Trusting a remote registry

import { parseRegistry, validateModule, verifyRegistrySignature } from "@suluk/builder";

const parsed = parseRegistry(await (await fetch(url)).json());   // rejects malformed/hostile entries (surfaced, not hidden)
parsed.modules;    // only the well-formed ModuleEntry[]
parsed.rejected;   // [{ title, reason }] — why each was refused

// for signed registries, verify against a pinned publisher public key before trusting:
const trusted = await verifyRegistrySignature(registryValue, signatureB64, publisherPublicKeyJwk);

API

| Export | What it does | | --- | --- | | buildApp(spec) | The headline: entities → { backend: { routes, document }, frontend: { components, pages }, registry, errors }. | | crudRoutesFromSchema(name, schema, defs?) | The 5 CRUD RouteContracts for one entity (the unit behind the backend). | | formBlock / tableBlock / crudSection / appPage | The DSL documents for each tier (blocks → section → page). | | registry / emptyRegistry / findDoc / allowedTypes | Build and query the document registry the validator/renderer resolves against. | | validateDocument / validateAll | The contract-narrowing + tier-rule checks → DslError[]. | | resolveParams / resolveList | A document's effective param values / a list param's ordered selection. | | renderPageTsx / resolveComponents | Render a page document to frontend TSX / the ordered component names it resolves to. | | toShadcnRegistry(app, opts?) | Package the built app as a shadcn registry (one block per entity + a page item). | | installModule / previewInstall / namespaceModule / gradeModule | Install / preview / collision-resolve / grade a SulukModule. | | planComposition / composeModules | Dependency-order and install a set of modules into one platform contract. | | AUTH / ECOMMERCE / CRM / BILLING / MARKETING | The curated first-party modules (+ FIRST_PARTY_REGISTRY). | | STACK_TEMPLATES / resolveTemplate | Named module sets ("SaaS starter", "Storefront", …) and their resolver. | | PREVIEW / PREVIEW_ONLY_MARKER | The deploy-only role-login fragment (deliberately excluded from the registry/templates). | | buildMarketing / marketingPage / seoMeta / jsonLd | The marketing section tier + SEO meta + schema.org JSON-LD. | | PROVIDER_CATALOG / readProviders / swapProvider / providerFacets | Read and swap provider slots (payments/auth/email/storage). | | parseRegistry / validateModule | Validate an untrusted registry fetched from a URL, surfacing rejected entries. | | signRegistry / verifyRegistrySignature / generateSigningKeypair / isSignedEnvelope | ECDSA P-256 publisher signatures for the open marketplace. |

The package has a single entry point — @suluk/builder (import { … } from "@suluk/builder"). There are no sub-path exports and no CLI.

Boundary

This package renders and generates — it never hosts (the Suluk L3 line). buildApp/renderPageTsx/ toShadcnRegistry emit source and documents you own and can edit (TSX components, a v4 document, a shadcn registry); the module functions return a merged contract value. Nothing here calls home or stands up a runtime.

Everything is pure over its inputs: buildApp takes entity schemas (you bring the Drizzle/Zod source), the registry/remote functions take the bytes (you fetch the JSON), and the generated frontend wires to the @suluk/nano-stores client at a baseUrl you supply. Database access, the actual route handlers, custom business rules, and the leaf UI components stay app-side — the package composes the contract and the scaffolding, not the irreducible 20%.

New reusable blocks, sections, and modules belong in the first-party registry here; app-specific compositions stay in your app.

License

Apache-2.0