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

@growth-labs/feature-requests

v0.1.0

Published

Network-wide feature-request submission: a Turnstile-guarded handler that lands requests in a consumer-owned, reviewable D1 queue (never auto-published). Ships an Astro integration plus a framework-agnostic core handler.

Readme

@growth-labs/feature-requests

Network-wide feature-request submission for Growth Labs sites. A Turnstile-guarded handler accepts a feature request (title, body, optional contact email) and lands it in a reviewable, consumer-owned D1 queue with status = 'pending'. Nothing is ever auto-published — moving a request forward is a separate review action the site owns.

The package is site-agnostic: each site injects its own D1 binding and Turnstile configuration. It ships two surfaces:

  • An Astro integration (default export) that injects the submission route.
  • A framework-agnostic handler (createFeatureRequestHandler) for bare Workers or custom routing.

Install

pnpm add @growth-labs/feature-requests

Astro integration

// astro.config.mjs
import featureRequests from '@growth-labs/feature-requests'

export default defineConfig({
  integrations: [
    featureRequests({
      // Public site key for the client-side Turnstile widget (safe to expose).
      turnstileSiteKey: '0x4AAA...',
      // Binding NAMES the site wires in wrangler.toml (never values):
      d1Binding: 'SITE_DB',                       // default
      turnstileSecretBinding: 'TURNSTILE_SECRET_KEY', // default
      // Optional:
      routePath: '/api/feature-requests',         // default
      tableName: 'gl_feature_requests',           // default
    }),
  ],
})

Bindings (wrangler.toml)

# Consumer-owned D1 — the site supplies the database.
[[d1_databases]]
binding = "SITE_DB"
database_name = "your-site-db"
database_id = "..."

# Turnstile secret via Cloudflare Secrets Store (binding NAME, never a value).
[[secrets_store_secrets]]
binding = "TURNSTILE_SECRET_KEY"
store_id = "..."
secret_name = "turnstile-secret-key"

The package resolves either binding shape (a Secrets Store SecretsStoreSecret or a legacy plain-string secret) server-side at request time, so the secret value never enters the build or any serialized config.

Schema

Apply the migration to your D1 binding before mounting the route:

wrangler d1 migrations apply SITE_DB \
  --local --migrations-path node_modules/@growth-labs/feature-requests/migrations

This creates gl_feature_requests:

| Column | Type | Notes | |--------------|------|----------------------------------------| | id | TEXT | UUID primary key | | title | TEXT | required | | body | TEXT | required | | email | TEXT | nullable (optional contact) | | status | TEXT | always 'pending' on insert | | created_at | TEXT | ISO-8601 |

Submitting

curl -X POST https://yoursite.com/api/feature-requests \
  -H 'content-type: application/json' \
  -d '{"title":"Dark mode","body":"Please add a dark theme.","email":"[email protected]","turnstileToken":"<widget-token>"}'

Responses:

| Status | code | Meaning | |--------|---------------------------|------------------------------------------| | 201 | — | { success, id, status: 'pending' } | | 400 | INVALID_BODY | title/body/email failed validation | | 400 | MISSING_TURNSTILE_TOKEN | no token in the payload | | 403 | TURNSTILE_FAILED | token rejected by Cloudflare | | 403 | TURNSTILE_NOT_CONFIGURED| no secret resolved (fail-closed) | | 502 | STORAGE_FAILED | D1 insert threw |

Framework-agnostic handler

For a bare Worker (no Astro), resolve the bindings yourself and build a handler:

import { createFeatureRequestHandler } from '@growth-labs/feature-requests/handler'

export default {
  async fetch(request, env) {
    if (request.method === 'POST' && new URL(request.url).pathname === '/api/feature-requests') {
      const handler = createFeatureRequestHandler({
        db: env.SITE_DB,                       // consumer-owned D1
        turnstileSecret: env.TURNSTILE_SECRET_KEY, // Secrets Store binding or string
      })
      return handler(request)
    }
    return new Response('Not found', { status: 404 })
  },
}

Design notes

  • Fail-closed. If no Turnstile secret resolves, every submission is rejected with TURNSTILE_NOT_CONFIGURED rather than silently accepting unverified traffic.
  • Bound, never interpolated. Field values are always bound parameters. The table name is validated as a safe SQL identifier at handler construction.
  • Reviewable, not published. The queue is the deliverable; the package never performs publish/accept/reject transitions.