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

@samyx/repl-orpc

v0.0.3

Published

Turn an oRPC contract + link into @samyx/repl-builder commands — a CLI, REPL, TUI or web UI over your RPC procedures

Readme

@samyx/repl-orpc

Turn an oRPC contract into @samyx/repl-builder commands — one per procedure — so your RPC API becomes a CLI, a REPL, a TUI, or a web UI without writing an argument parser.

npm i @samyx/repl-orpc @orpc/client @orpc/contract

Usage

#!/usr/bin/env node
import { RPCLink } from '@orpc/client/fetch';
import { createCli } from '@samyx/repl-builder';
import { createOrpcCommands } from '@samyx/repl-orpc';
import { z } from 'zod';
import { contract } from './contract';

const commands = createOrpcCommands({
  contract,
  link: new RPCLink({ url: 'http://localhost:3000/rpc' }),
  toJsonSchema: (schema) => z.toJSONSchema(schema, { io: 'input' }),
});

process.exit(await createCli(commands, { name: 'planets' }).run());
$ planets                              # lists every procedure in the contract
$ planets help planet.find             # usage, built from the input schema
$ planets planet.find --id 4           # → calls client.planet.find({ id: 4 })
$ planets planet.find --input '{"id":4}'

Commands are named by their router path (planet.find), so nesting is preserved. route({ summary }) becomes the command description; without one, the dotted path is used — most contracts set no route metadata, so that is the common case, not a rare fallback.

Subcommand style

Nested procedures are dotted by default (planet.find). Set style: 'subcommand' for git-style grouping instead:

createOrpcCommands({ contract, link, style: 'subcommand' });
$ planets planet find --id 1
$ planets planet                 # lists the group
$ planets planet help find       # ┐ all three show the same usage
$ planets help planet find       # ├
$ planets planet find --help     # ┘

Note that the top-level listing stays flat: planets with no arguments prints every procedure (planet find, planet list, …), not one row per group. On a large contract that is a long list — reach for filter, or ship a hand-written top-level help via epilogue, if that matters to you.

Both styles are just a separator — createCli resolves the longest registered name matching a prefix of argv, so a space-separated name is a subcommand path. For anything else, name takes a router path and returns whatever you want:

name: (path) => path.join(':'),          // planet:find
name: (path) => path.at(-1)!,            // flat — find, list (beware collisions)

Any link works

The transport is entirely the link's business — this package only dispatches. RPCLink (fetch), OpenAPILink, WebSocket, and message-port links all work unchanged, as does anything implementing ClientLink:

import { RPCLink } from '@orpc/client/websocket';
const link = new RPCLink({ websocket: new WebSocket('ws://localhost:3000') });

Flags come from the input schema

Standard Schema exposes validation, not structure — so flattening an input into flags needs a converter from whatever schema library the contract uses. Pass one via toJsonSchema:

| Library | Hook | |---------|------| | Zod 4 | (s) => z.toJSONSchema(s, { io: 'input' }) | | Valibot / ArkType | oRPC's *ToJsonSchemaConverter, e.g. (s) => new ValibotToJsonSchemaConverter().convert(s, { strategy: 'input' })[1] |

Without a converter there are no per-field flags at all--input '<json>' is the only way in, and help <cmd> will show just that. That is the honest ceiling; the whole package works, it is only the ergonomics that degrade.

Only flat, scalar top-level properties become flags. Anything nested still goes through --input. Flattened flags are coerced (--id 44, not "4") and enums are validated before anything leaves the process. --input and flattened flags compose — --input is the base, flags overlay it.

Options

| Option | Default | | |--------|---------|--| | contract | — | The contract router. | | link | — | Any oRPC ClientLink. | | toJsonSchema | — | Structure-aware flags (see above). | | style | 'dot' | 'dot'planet.find; 'subcommand'planet find. | | name | from style | Command name from a router path. Overrides style. | | filter | — | (entry) => boolean, to expose a subset of procedures. | | command | — | (command, entry) => CommandDef — last word on each command: aliases, descriptions, wrapping execute. | | inputFlag | 'input' | Flag carrying the whole input as JSON. Reserved: an input property of the same name is exposed as --input.<name>. |

Errors

An ORPCError prints as <command>: <code> — <message>, plus its data when present, and exits 1. Argument errors (unknown flag, bad enum, non-integer) are caught locally and exit 2 without dispatching.