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

@molecule/api-resource-project

v1.0.1

Published

User project management with sandbox assignment

Readme

@molecule/api-resource-project

Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit src/index.ts JSDoc, not this file.

Owner-scoped project resource for molecule.dev — CRUD handlers, routes, and an authUser object-level authorizer, wired via routes + requestHandlerMap.

Quick Start

import { routes, requestHandlerMap } from '@molecule/api-resource-project'
// POST|GET /projects · GET|PATCH|DELETE /projects/:id — registered by
// mlcl inject, or manually:
// for (const r of routes) app[r.method](r.path, requestHandlerMap[r.handler])

Type

resource

Installation

npm install @molecule/api-resource-project @molecule/api-bond @molecule/api-database @molecule/api-i18n @molecule/api-locales-project @molecule/api-logger @molecule/api-resource

API

Interfaces

BrandingSpec

Branding applied to a scaffolded project. Derived from user cues gathered during discovery, or randomized so generated apps are visually distinct.

interface BrandingSpec {
  /** Display name (header, auth pages, PWA manifest, OG tags). */
  appName: string
  /** Primary brand color as a hex string (e.g. `#1a8917`). */
  brandColor: string
  /** Short description used in the PWA manifest and meta tags. */
  appDescription: string
  /** About/footer link. */
  websiteUrl?: string
  /** Logo dimensions in pixels. */
  logoSize?: number
  /** Whether the spec came from explicit user cues or was randomized. */
  source: 'user' | 'random'
}

Project

A user project record (name, type, framework, packages, sandbox state, settings).

interface Project {
  id: string
  userId: string
  name: string
  slug: string
  projectType: 'api' | 'app' | 'full-stack' | 'status-page'
  framework: string | null
  packages: string[]
  /** Flagship template slug the project was scaffolded from, if any. */
  templateSlug: string | null
  /** Branding chosen for the project during discovery, if any. */
  brandingSpec: BrandingSpec | null
  envVars: Record<string, string>
  sandboxId: string | null
  sandboxStatus: 'creating' | 'queued' | 'running' | 'sleeping' | 'stopped'
  lastActiveAt: string | null
  settings: Record<string, unknown>
  createdAt: string
  updatedAt: string
}

Types

CreateProjectInput

Create Project Input type.

type CreateProjectInput = Pick<Project, 'name' | 'projectType'> & {
  framework?: string
  packages?: string[]
}

UpdateProjectInput

Update Project Input type.

framework, packages, projectType, templateSlug, and brandingSpec are writable so the post-discovery selection step can persist the chosen starting point before the sandbox boots.

type UpdateProjectInput = Partial<
  Pick<
    Project,
    | 'name'
    | 'settings'
    | 'envVars'
    | 'sandboxId'
    | 'sandboxStatus'
    | 'framework'
    | 'packages'
    | 'projectType'
    | 'templateSlug'
    | 'brandingSpec'
  >
>

Functions

authUser(req, res, next)

Object-level authorization middleware for a single project (:id).

Fails closed: looks up the project scoped to BOTH the :id route param and the authenticated session.userId, so a row is returned only when the caller owns it. On success the owned row is stashed in res.locals.project for the downstream handler (avoiding a second query); otherwise the request is rejected with 401 (no session) or 403 (project missing or owned by someone else — the two are deliberately indistinguishable so existence is not leaked).

This is the shipped default for the read/update/delete routes (see routes.ts), mirroring @molecule/api-resource-device's authUser. A consumer with a richer access model (e.g. owner-or-team) may gate the route with its own middleware instead and set res.locals.project to the pre-authorized row.

function authUser(
  req: MoleculeRequest,
  res: MoleculeResponse,
  next: MoleculeNextFunction,
): Promise<void>
  • req — The request object (uses params.id).
  • res — The response object (reads locals.session, writes locals.project).
  • next — Passes control to the next handler on success.

create(req, res)

Creates a new project with a unique slug derived from the project name. Requires name and projectType in the request body. Optionally accepts framework and packages. Appends a timestamp suffix to the slug if it already exists.

function create(req: MoleculeRequest, res: MoleculeResponse): Promise<void>
  • req — The incoming request with CreateProjectInput body and userId.
  • res — The response object for sending the created project or error.

del(req, res)

Del.

function del(req: MoleculeRequest, res: MoleculeResponse): Promise<void>
  • req — The request object.
  • res — The response object.

list(_req, res)

Lists the authenticated caller's projects, newest first. Scoped to session.userId so a generated app never returns another tenant's rows — an unscoped list is a one-request full-tenant data dump. Returns 401 when there is no authenticated session.

function list(_req: MoleculeRequest, res: MoleculeResponse): Promise<void>
  • _req — The request object.
  • res — The response object (reads locals.session).

read(req, res)

Reads a single project the caller owns. The authUser route middleware already loads the owner-scoped row into res.locals.project; this handler reuses it when present and otherwise falls back to its own owner-scoped lookup, so it fails closed even if mounted without that middleware. Returns 401 with no session and 404 when no project owned by the caller matches the id (existence is not leaked to non-owners).

function read(req: MoleculeRequest, res: MoleculeResponse): Promise<void>
  • req — The request object (uses params.id).
  • res — The response object (reads locals.session/locals.project).

update(req, res)

Update.

function update(req: MoleculeRequest, res: MoleculeResponse): Promise<void>
  • req — The request object.
  • res — The response object.

Constants

i18nRegistered

The i18n registered.

const i18nRegistered: true

requestHandlerMap

Map of request handler names to implementations. authUser is the object-level authorization middleware referenced by routes.ts for the read/update/del routes.

const requestHandlerMap: {
  readonly create: typeof create
  readonly list: typeof list
  readonly read: typeof read
  readonly update: typeof update
  readonly del: typeof del
  readonly authUser: typeof authUser
}

routes

Route array for project CRUD: POST create, GET list, GET/:id read, PATCH/:id update, DELETE/:id del.

create/list only need an authenticated session (authenticate); the list handler itself scopes results to the caller's userId. The object-level routes (read/update/del) are gated by authUser, which fails closed — it loads the project scoped to the caller's userId and 401/403s otherwise — so generated apps that wire this resource do NOT expose other tenants' projects by default. Mirrors @molecule/api-resource-device.

const routes: readonly [
  {
    readonly method: 'post'
    readonly path: '/projects'
    readonly handler: 'create'
    readonly middlewares: readonly ['authenticate']
  },
  {
    readonly method: 'get'
    readonly path: '/projects'
    readonly handler: 'list'
    readonly middlewares: readonly ['authenticate']
  },
  {
    readonly method: 'get'
    readonly path: '/projects/:id'
    readonly handler: 'read'
    readonly middlewares: readonly ['authUser']
  },
  {
    readonly method: 'patch'
    readonly path: '/projects/:id'
    readonly handler: 'update'
    readonly middlewares: readonly ['authUser']
  },
  {
    readonly method: 'delete'
    readonly path: '/projects/:id'
    readonly handler: 'del'
    readonly middlewares: readonly ['authUser']
  },
]

Injection Notes

Requirements

Peer dependencies:

  • @molecule/api-bond ^1.0.1
  • @molecule/api-database ^1.0.1
  • @molecule/api-i18n ^1.0.1
  • @molecule/api-locales-project ^1.0.1
  • @molecule/api-logger ^1.0.1
  • @molecule/api-resource ^1.0.1

Runtime Dependencies

  • @molecule/api-bond
  • @molecule/api-database
  • @molecule/api-i18n
  • @molecule/api-locales-project
  • @molecule/api-logger
  • @molecule/api-resource

The shipped routes are owner-scoped and fail closed — they do not expose other tenants' projects by default. GET /projects (list) returns only the authenticated caller's rows (scoped to session.userId), and the object-level routes GET/PATCH/DELETE /projects/:id are gated by the authUser authorizer, which loads the project scoped to the caller's userId, stashes it on res.locals.project, and responds 401 (no session) / 403 (not the owner) otherwise. This mirrors @molecule/api-resource-device. A consumer that needs a richer access model (e.g. owner-or-team) can gate the route with its own middleware and set res.locals.project to the pre-authorized row — read, update, and del reuse it instead of re-deriving ownership.

Table: src/__setup__/projects.sql creates projects. An mlcl-scaffolded API replays __setup__/*.sql automatically on migrate; anywhere else run it once. User-facing strings use t(key, …, { defaultValue }); translations ship in the companion @molecule/api-locales-project bond.

E2E Tests

Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip. Project is strictly OWNER-scoped: a project belongs to exactly one user (userId), with no members, collaborators, or roles — so every check is about the owner seeing/mutating only their own rows, never a shared grant:

  • [ ] Creating a project persists its real fields (name → derived slug, projectType, framework, packages) and it appears at the top of the owner's project list (the list is scoped to the session user, newest-updated first).
  • [ ] Editing reflects and persists: renaming updates the name; a single-key settings/envVars PATCH MERGES onto the stored bag without wiping sibling keys; a sandboxStatus change round-trips. Reload — the changes survive.
  • [ ] Deleting a project removes it from the owner's list and a re-fetch of its id no longer returns it; there are no members to notify or re-scope.
  • [ ] Authorization — the list and every :id route return ONLY projects the caller owns. Signed in as a second user (or guessing another user's project id), GET/PATCH/DELETE /projects/:id is refused (403/404) and the row is neither readable nor mutable; existence is not leaked to a non-owner.