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

@amadeni/dev-contract

v0.2.0

Published

Standardized dev-start/dev-auth process contract for the Amadeni project fleet: ready means a verified login

Readme

@amadeni/dev-contract

Standardized dev-start/dev-auth process contract for the Amadeni project fleet. One CLI replaces the per-repo shell scripts (dev-start.sh / dev-auth.sh / dev-stop.sh) that every project used to copy — with one core guarantee the scripts never gave:

ready = verified login. dev-contract start only reports ready after a dev login has DEMONSTRABLY worked: it mints a single-use token, consumes it at the magic link verify endpoint, and replays the issued cookies against an authenticated probe until the response proves a live session. The pipeline receives a ready-made authenticated state (cookies + Convex JWT), not just URLs — the "screenshot shows the login screen instead of the app" failure mode cannot pass the gate.

What start does

  1. Starts convex dev (detached process group, pid + log files in .dev-contract/). Fresh checkouts get CONVEX_AGENT_MODE=anonymous so Convex picks a local anonymous deployment without prompting. Readiness means the backend answers and convex dev has reported Convex functions ready for this start — on a fresh deployment the push lands seconds after the backend, and nothing (seed, token mint) calls a function before it did.
  2. Guard (hard abort): provisioning only ever happens against a dev:* or anonymous:* CONVEX_DEPLOYMENT (and only local CONVEX_SELF_HOSTED_URL hosts). Anything else exits non-zero before a single env var is written.
  3. Provisions missing dev env vars on the Convex deployment: AMADENI_DEV_AUTH_ENABLED=true, a generated BETTER_AUTH_SECRET, and SITE_URL — reconciled on every start, so repaired environments heal.
  4. Seed (optional): runs the base seed profile — after the backend is ready and provisioned, before the login gate — so the test user / base data exist before the login is verified. A failing seed aborts the start with a [seed] diagnosis; there is no "ready" on top of a broken seed. Other profiles (full) only run on request. See Seeding.
  5. Starts the app dev server and waits for HTTP.
  6. Readiness gate: retries mint → verify → session-probe until the login is verified (or the deadline passes — then it fails loudly with the step that broke). A second, unused token becomes auth.loginUrl for browser consumers.
  7. Emits the contract JSON as the last stdout line (all logging goes to stderr):
{
  "ok": true,
  "baseUrl": "http://localhost:3001",
  "appUrl": "http://localhost:3001",
  "convexUrl": "https://<deployment>.convex.cloud",
  "convexSiteUrl": "https://<deployment>.convex.site",
  "auth": {
    "email": "[email protected]",
    "cookie": "better-auth.session_token=...; better-auth.convex_jwt=...",
    "cookies": {
      "better-auth.session_token": "...",
      "better-auth.convex_jwt": "..."
    },
    "convexJwt": "<decoded JWT for ConvexHttpClient.setAuth()>",
    "loginUrl": "http://localhost:3001/api/auth/magic-link/verify?token=..."
  },
  "readyAt": "2026-01-02T03:04:05.000Z",
  "pids": { "convex": 123, "app": 456 },
  "stateDir": "/abs/path/.dev-contract"
}

Failures never emit ok: true: the process exits non-zero with a [step]-prefixed diagnosis on stderr (guard, convex-ready, provision, seed, app-ready, mint-token, verify, session-probe, login-ready, ...).

Commands

dev-contract start [--config path] [--email x] [--out file] [--root dir]
dev-contract auth   # fresh verified session for a running environment
dev-contract seed [--profile <name>]   # run one seed profile (default: base)
dev-contract stop   # stop the process groups started by `start`

auth emits { "ok": true, "loginUrl": ..., "baseUrl": ..., "auth": {...} }; seed emits { "ok": true, "profile": "full", "ran": ["command", "function"] }; stop emits { "ok": true, "stopped": [...] }.

Seeding (optional)

Projects that need base data (a test user, org fixtures, e2e profiles) before the first login declare a seed block in the config. The top-level command / function / args are the base profile; further profiles live in seed.profiles:

{
  "seed": {
    "command": "pnpm run seed:dev",
    "function": "testSupport/seed:ensureBaseData",
    "args": { "profile": "e2e" },
    "profiles": {
      "full": {
        "function": "testSupport/seed:ensureFixture",
        "args": { "scenario": "review" }
      }
    }
  },
  "timeouts": { "seedMs": 300000 }
}
  • command is run as a shell command in the project root.
  • function is run via npx convex run (typecheck/codegen disabled, auth.identity attached when configured — identity-gated seed functions work exactly like the token function).
  • Every profile needs at least one of the two; with both set, command runs first.
  • Profiles. base is what start runs (after backend readiness + provisioning, before the auth/login gate) and what dev-contract seed runs without --profile. profiles.base may replace the top-level block, but declaring both is a config error. Any other name only ever runs on request: dev-contract seed --profile <name>. A block with only profiles (no base) is fine — start then seeds nothing.
  • full is the fleet convention for the complete test fixture (just dev-seed full in the fleet contract): Mynd's executor runs it once after dev-start and before dev-auth, with a 5-minute budget, one attempt, failure = warning. Profile names are shell-safe ([A-Za-z0-9_-]).
  • timeouts.seedMs (default 300 000 ms) is the budget for one seed profile — it applies to command and to function each. A timeout kills the process and fails the seed with [timeout] in the diagnosis.
  • An unknown profile fails with [seed] unknown profile <name> on stderr and a non-zero exit — never a silent no-op.
  • Every profile MUST be idempotent (insert-only, or probe-then-insert like the Hub's ensure_seed): the contract re-runs base on every start and full on every review iteration. Wipe-and-recreate seeds do not belong here; full should be additive on top of base.
  • Any seed failure is a hard abort with a [seed]-prefixed diagnosis — the environment is never reported ready on a broken seed.
  • The deployment guard applies: seeding (like everything that writes) is only ever allowed against dev:* / anonymous:* deployments.

Project integration

1. Config: devcontract.config.json in the repo root

See devcontract.config.example.json. Minimal version:

{
  "appUrl": "http://localhost:3001",
  "auth": {
    "createTokenFunction": "dev/auth:createDevToken",
    "identity": { "issuer": "my-app-dev-auth", "subject": "dev-auth-cli" }
  }
}

Everything else has defaults (pnpm, convex dev, next dev -p <port>, better-auth verify/get-session paths, 120s/120s/90s timeouts, 300s per seed profile).

2. Convex-side fixture: createDevAuth from @amadeni/better-auth-kit

The token function referenced by auth.createTokenFunction lives in the app's convex/ directory and is a thin wiring of the kit factory (v0.3.0+). It writes a hashed magic-link verification row directly into the Better Auth component — the login then runs through the app's regular verify endpoint, with real sessions and cookies:

// convex/dev/auth.ts
import { v } from 'convex/values';
import {
  createDevAuth,
  requireDevAuthCliIdentity,
} from '@amadeni/better-auth-kit';
import { action } from '../_generated/server';
import { components, internal } from '../_generated/api';

const devAuth = createDevAuth({
  createVerification: (ctx, input) =>
    ctx.runMutation(components.betterAuth.adapter.create, { input }),
  ensureUser: (ctx, { email, name }) =>
    ctx.runMutation(internal.dev.auth.ensureDevUserInternal, { email, name }),
});

export const createDevToken = action({
  args: { email: v.optional(v.string()) },
  handler: async (ctx, args) => {
    await requireDevAuthCliIdentity(ctx, {
      issuer: 'my-app-dev-auth', // must match devcontract.config.json
      subject: 'dev-auth-cli',
    });
    return await devAuth.issueToken(ctx, args);
  },
});

The kit enforces the hard gate: minting throws unless AMADENI_DEV_AUTH_ENABLED === 'true', and always throws on production-shaped deployments. Never set that variable on production.

Apps with existing dev-auth actions (e.g. the Hub's dev/auth:createDevToken) work as-is — the contract only requires "takes { email? }, returns { token }".

3. Optional: keep the just recipes as thin wrappers

dev-start:
    pnpm exec dev-contract start

dev-auth:
    pnpm exec dev-contract auth

dev-seed profile='base':
    pnpm exec dev-contract seed --profile {{profile}}

dev-stop:
    pnpm exec dev-contract stop

Consumer notes (Mynd / pipelines)

  • Legacy compatibility: the previous shell contract emitted {"baseUrl": ...} (dev-start) and {"loginUrl": ...} (dev-auth) as the last stdout line. The new output is a strict superset: baseUrl stays top-level in start, loginUrl stays top-level in auth. Existing parsers (parseDevStartOutput / parseDevAuthOutput) keep working unchanged.
  • The upgrade: consumers should switch from "open loginUrl and hope" to injecting the delivered state directly — set auth.cookie as the Cookie header (or seed the browser context's cookies) and/or use auth.convexJwt with ConvexHttpClient.setAuth(). loginUrl remains for pure-browser flows; it carries a fresh unused single-use token.
  • Trust the exit code, not the log tail: exit 0 + last-line JSON with ok: true is the only ready signal; the JSON is only emitted after the verified-login gate passed. On failure the exit code is non-zero and stderr names the failing step.
  • start is idempotent: running processes are reused, env state is re-reconciled, and the login is re-verified on every call — safe to call once per review iteration.
  • Test fixture: dev-contract seed --profile full (= just dev-seed full) between start and auth; the last stdout line is { "ok": true, "profile": "full", "ran": [...] }. Treat a non-zero exit as a warning about the fixture, not as "environment not ready".

Programmatic use

import { loadConfig, runStart } from '@amadeni/dev-contract';

const config = await loadConfig(projectRoot);
const result = await runStart(config); // throws DevContractError with .step

Security posture

  • Provisioning is hard-gated to dev:* / anonymous:* deployments — the CLI refuses everything else before writing anything.
  • The dev login itself is additionally gated Convex-side by @amadeni/better-auth-kit's assertDevAuthEnabled (exact-match env flag + production-shape refusal).
  • Zero runtime dependencies; Node >= 20.

Development

pnpm install
pnpm run ci    # prettier + eslint + tsc + cspell + vitest