npxio/ npx

|npx

A general-purpose language where data flows left to right, like a shell pipe. Failures are values you handle on the same line, and & runs work in the background.

New to npx? Learn it in 12 short lessons →
version 0.16.0
compiled to JavaScript in your browser
early: the syntax can still change

Press Run, or ctrl+enter.
Add files with + and load them with use ./name. Blocks are made by indentation.Your code stays in this page; nothing is sent anywhere unless the program asks the network.

tour

Six ideas make up npx. Each one has a short example; "Load it" puts it in the playground above.

Pipes come first

a | f x means f x a: the value on the left goes in as the last argument. Every built-in puts its data last, so pipelines read top to bottom.

.name on its own is a function that reads that field.

orders
  | keep (fn o -> o.paid)
  | sort_by .total
  | map .total
  | sum
  | print

No brackets to call

Write greet "aku", not greet("aku"). Give a function fewer arguments than it needs and you get a new function that waits for the rest.

fn add a b = a + b
inc = add 1
print (inc 41)        # 42

double_all = map (fn x -> x * 2)
print (double_all [1, 2, 3])

Errors are values

fail "msg" signals that something didn't work. or catches it on the same line, so there's no try or catch.

Real bugs, like adding text to a number, aren't caught by or, so they can't hide.

age = int "forty" or 0
nick = user.nick or user.name
page = net.get url or fail "offline"

match takes things apart

Match on numbers, ranges, text, list shapes, record fields and types, with an if guard when a shape isn't enough.

match x
  0 -> "zero"
  1..9 -> "small"
  [first, ...rest] -> "a list"
  { name } if name != "" -> "hi {name}"
  _ -> "something else"

& runs it in the background

Like a terminal job: & starts work and hands you a job right away. wait gets the result, or the results of a whole list of jobs. Live streams work the same way: every 1000 | map f | each print runs in the background until you stop it.

tea = & brew "tea" 600
coffee = & brew "coffee" 900
wait [tea, coffee] | each print

Modules look like paths

The standard library lives under ~/io, your own files load with use ./name, and any npm package with use npm/name. JavaScript classes, methods, promises and callbacks all work from npx.

use ~/io/net
use ./helpers
use npm/discord.js
use node/crypto

reference

values
42 3.14 "text {expr}" yes no none [1, 2] { name: "x" } 1..10 fn x -> x * 2
names
x = 1 can't change. mut n = 0 then n <- n + 1 can. [a, b] = pair and { name } = user take values apart.
functions
fn name a b = body, with optional types: fn add a:int b:int -> int = a + b. Types: int num text bool list rec fn none job any. ret returns early.
control
if c then a else b or an indented block, match, for x in xs, while c, break, next
truth
Only no and none count as false. 0 and "" are values like any other.
lists
map keep drop each fold find any all count sum min max first last take skip slice reverse sort sort_by uniq uniq_by flat zip push enum has len index_of chunk window group_by count_by sum_by min_by max_by
text
split join lines chars upper lower trim replace starts ends pad has len text int num
records
keys values get set has, { ...r, x: 1 }, r1 + r2
big numbers
Whole numbers of any size: 123n, 0xffn, big "1234…". + - * // % and comparisons work, mixing with whole numbers; bit operations too. big_le bytes and le_bytes n x convert to and from little-endian bytes. Type big.
bits
xor band bor bnot shl shr rotl rotr add32 sub32 mul32 u32 bits hex32. All work on unsigned 32-bit words.
bytes
hex unhex utf8 unutf8 words unwords, where bytes are a list of numbers 0 to 255
streams
Live values that flow through pipes: sources every ms, events "name" emitter (any JS event emitter, a discord.js client included), stream list, merge [s1, s2]. map keep drop take skip work on streams; window n, debounce ms, throttle ms. s | each f runs it in the background and gives a job (wait job, stop job); collect s and fold wait for the end. A failing item is reported and the stream carries on.
web server
On the command line, use ~/io/http: http.serve 8080 (http.router { "GET /users/:id": fn req -> { id: req.params.id } }). Handlers return text, a record (sent as JSON), { status, body, headers } or none. A failing handler answers 500 and the server keeps running.
tests
Top-level functions named test_… in *_test.npx files are tests: fn test_add = expect (add 1 2) 3. expect_fail f checks that f fails. Run them with npx --test.
command line
npx file.npx runs a program; npx --test, npx --check, npx --fmt, npx --trace, npx --audit; npx --build file.npx makes one .mjs file that runs with plain node, for copying to a server, and npx --build --exe file.npx one program file with Node inside; npx --js shows the compiled JavaScript; npx alone opens a prompt.
trace
The Trace button (or npx --trace file.npx) runs the program and shows the value after every | stage, with its line, so you can watch data flow through a pipeline.
format
The Format button (or npx --fmt file.npx) lays code out the standard way: 2-space indentation, spaces around operators and after commas. It never changes what the code does. npx --fmt --verify only reports, for CI.
playground
main.npx runs; + adds a file that main.npx loads with use ./name. The JavaScript tab shows what the open file compiles to. Share keeps every file. Keys: ctrl+enter runs, ctrl+s formats, ctrl+/ comments lines.
web pages
use ~/io/page: a page is a function that answers elements; after every click or keystroke npx calls it again and updates what changed. page.show view, page.div [kids], page.h1, page.p, page.row, page.list, page.button "label" f, page.input { value, on_input: fn text -> … }, page.link, page.img, page.el tag props kids, page.style css, page.refresh(). It shows in the Page tab here; npx --build --web file.npx makes a standalone .html.
shared libraries
On the command line, npx --add github:user/repo adds a library anyone published, pinned in npx.json; then use lib/name. npx --install fetches them again and checks nothing changed.
check
The Check button (or npx --check) finds likely mistakes without running: too many arguments, a call on its own line missing arguments, type mistakes it can be sure of (text plus a number, a record field that doesn't exist, the wrong type for a typed parameter or -> type, comparing text with a number), a value named like a built-in then called, unused names and use lines (start a name with _ to keep it unused on purpose).
audit
The Audit button (or npx --audit file.npx) lists what a program can touch (network hosts, files, environment variables, npm and Node modules, background work) without running it, following into libraries. allow net "discord.com" lines at the top of a file lock it to exactly that; anything else is refused. Kinds: net, read, write, env, npm, node.
other
print show type fail attempt wait tap range repeat chunk round floor ceil abs. attempt f runs f and returns { ok, value } or { ok: no, error }, catching bugs too.
npx libraries
use npx/discord: Discord bots as one record (commands, slash commands, events), with replies returned not sent, intents worked out for you, cooldowns, admin-only commands, and discord.test to try a bot without Discord. use npx/fetch: HTTP with JSON in and out, timeouts and retries: api = fetch.client { base: "https://…" }, api.get "/users/1", api.post "/users" { name: "aku" }, and fetch.fake for tests without a network. Beta, tested against stand-ins but not yet against the real services: use npx/csv: csv.parse text gives records (header row as field names), csv.text records writes CSV. use npx/mail (command line): post.send { to, subject, text } through Resend or SMTP. use npx/stripe (command line): Stripe payments. use npx/s3, npx/sheets (command line, beta): file storage on any S3-compatible service, and Google Sheets as records. use npx/kv: a key-value store, store.add "coins:aku" 25, store.set_for 60000 "cooldown:aku" yes, store.top "coins:" 3; kv.memory() here, kv.open "bot.json" on the command line. use npx/ai: model.ask, model.chat and model.json for Claude and OpenAI-style APIs, with ai.fake for tests. use npx/telegram: Telegram bots as one record (commands, buttons, on.message), nothing to install, telegram.test bot "/start" to try one. use npx/cron: scheduled jobs, cron.run "0 9 * * mon-fri" f, cron.run "every 5m" f, { when: …, zone: "Europe/Berlin" } for another time zone, daylight saving included, cron.upcoming to test a schedule. use npx/db (command line): SQLite with records in and out, bank.insert "users" { name: "aku" }, bank.find "users" { vip: yes }, transactions. use npx/lattice: the LATTICE cipher.
npm
use npm/pkg loads any npm package; use node/fs loads Node's own modules (command line). Calling a JS class builds an object; JS methods, getters, promises and Map/Set loops work; npx functions passed to JS become callbacks: pure ones answer immediately (so array.sort and lodash work), others answer with a promise (events, async APIs). type of a JS object is js. JavaScript's globals are in ~/io/js: js.Date(), js.Map().
~/io
net (get post json request query), json (parse text pretty), math (with bytes for random bytes), time (now sleep date), date (now parse iso day parts add_days add_hours days_between). Command line only: fs (read write list exists), sys (args env exit).

where it stands

Works today

Everything on this page: the full syntax, pattern matching, typed functions, background jobs, bit operations and the standard library, covered by 134 tests. The same compiler runs here and on the command line.

Fast

npx compiles to JavaScript. Functions that only compute run as plain synchronous JavaScript, about 5 times faster again than 0.3 and within a few times of hand-written JS. The LATTICE cipher written in npx, including its authenticated mode with Poly1305 on big numbers, matches all 26 official test vectors in under a fifth of a second.

Still changing

Syntax can change before 1.0. If something reads wrong, or you'd write it differently, tell us.