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

wawesome

v0.8.0

Published

CLI tool for building and deploying serverless functions on wawesome.io platform

Readme

🚀 wawesome

Official CLI for building, bundling, and deploying serverless WebAssembly functions on the wawesome.io platform.

npm version

Deploy ultra-fast, lightweight serverless WebAssembly functions directly from your terminal in under 60 seconds.


⚡ Quick Start

1. Create an account

Sign up for free at https://wawesome.io to set up your workspace.

2. Authenticate CLI

Login via browser OAuth:

npx wawesome login

3. Initialize a project

Create a new directory and scaffold a WebAssembly TypeScript starter function:

mkdir my-wasm-app && cd my-wasm-app
npx wawesome init

init asks for the function name and for the App slug — the App groups the Functions of one project, and its slug is part of the public URL your client sees. Both default to the directory name, so naming is usually a matter of pressing enter.

An App slug must be a legal hostname label: lowercase letters, numbers, and single hyphens between them (no leading or trailing hyphen, 63 characters at most). Type something else — My Client — and the CLI shows you the slug it would become (my-client) and asks again, rather than rewriting your answer behind your back.

Starting from a template

npx wawesome templates lists what is available; --template starts from one:

mkdir acme-store && cd acme-store
npx wawesome init --template stripe-webhook

This goes from nothing to a deployed Function in one command. The template is downloaded over plain HTTP and unpacked — git is not required, on any platform — and its files land exactly as they are published. Only the name in package.json, the App and Function names in wawesome-function.json, and the wawesome dependency are touched — the last so the project you get depends on the CLI that scaffolded it rather than the one the template was released against.

Templates declare what they need rather than shipping placeholders, so the CLI then asks for each environment variable the template requires — showing where in the provider's own dashboard to find the value — stores them (secrets write-only), enables any outbound providers the template calls, and deploys. If a value does not exist yet, leave it blank; the CLI tells you the env set command to run once it does.

Dependencies are installed for you (with whatever package manager launched the CLI — npx, pnpm dlx, bun x), so the types are there when you open the project and the bundler can resolve the template's imports. Pass --no-install to do it yourself. A failed install never stops the flow; the command to re-run is printed.

Before any of that is sent anywhere, the CLI shows you the public URL your Function will have and offers to change your workspace address — the part of that URL that is yours rather than this project's, and the same one wawesome workspace show prints. It is randomly minted at signup and stays changeable until your first deploy, at which point it locks for good, because live URLs carry it. Press enter to keep it. If it is already locked, the offer is not made and the reason is said plainly.

Being logged in is checked before the first question, not at the deploy: an expired session offers you a login there and then, and declining still leaves you the project plus the two commands that finish it.

Run it in an empty directory: if any file would be overwritten, nothing is written at all and the collision is named.

4. Build & Deploy

Deploy your serverless function to Wawesome Cloud instantly:

npx wawesome deploy

📖 Command Reference

| Command | Description | |:----------------------------------------|:--------------------------------------------------------------------| | npx wawesome login | Authenticate CLI with your Wawesome account via browser | | npx wawesome logout | Log out and clear saved credentials from your machine | | npx wawesome whoami | View current logged-in user, workspace, and gateway info | | npx wawesome templates | Browse the template catalog (no login required) | | npx wawesome init | Scaffold a new serverless function project in the current directory | | npx wawesome init --template <name> | Scaffold from a catalog template, wire up what it needs, and deploy | | npx wawesome build | Bundle TypeScript entry code into an optimized JS bundle | | npx wawesome deploy | Build, upload, and promote a function version to production | | npx wawesome logs [func] | List recent past invocations for a function | | npx wawesome logs --invocation <id> | Fetch full stdout/stderr log body for a specific invocation | | npx wawesome logs <func> --follow | Follow live output (waits for the next invocation if needed) | | npx wawesome invoke [func] | Fire a function run immediately and follow its output live | | npx wawesome cron [func] | List schedules for a function or app | | npx wawesome cron pause <name> | Pause a schedule by name (survives future deploys) | | npx wawesome cron resume <name> | Resume a paused schedule (no backfill) | | npx wawesome cron history [func] | Read background run history for scheduled and manual runs | | npx wawesome version list | List version history for the current function | | npx wawesome version switch <v> | Roll back or promote a specific function version | | npx wawesome env list | View environment variables for the current app | | npx wawesome env set <key> <val> | Set an environment variable (add --secret for write-only) | | npx wawesome env rm <key> | Delete an environment variable |


🧭 Unsupported Globals

Every build scans the bundle it just produced against the platform's declared guest surface, and says nothing unless it finds something.

  • Intl is not provided. Where the bundle reaches it as it loads — your own module scope, or a dependency's — the build is refused before a deploy uploads anything: that bundle would not evaluate on the platform. Where the reference sits inside a function that may never be called, behind a typeof check, or inside a try, you get a warning and the deploy proceeds.
  • toLocaleString, toLocaleDateString, toLocaleTimeString, toLocaleLowerCase and toLocaleUpperCase ignore their locale argument. They run and return an unlocalised answer, so these warn.

Each message names the global, the file and line in your source, and what to do about it. Declaring an Intl polyfill in your package.json — the same declaration local parity reads — silences the report, as does installing one on globalThis in the bundle itself. The scan reads static references only: a global reached through globalThis['Intl'] is invisible to it, so it never refuses a deploy on a guess. deploy --skip-build scans the bundle it found on disk before uploading it.

📜 Invocation Logs

Inspect past function runs or view raw stdout / stderr log outputs directly in your terminal.

1. List Recent Invocations

List past executions (including status, trigger type, timestamp, and duration) for the function in the current directory:

npx wawesome logs

Or list invocations for a specific function by name:

npx wawesome logs my-function

Filter by invocation status — only show errors, timeouts, etc.:

npx wawesome logs my-function --error
npx wawesome logs my-function --status timeout
npx wawesome logs my-function --running

2. View Invocation Log Body (stdout/stderr)

Fetch and print the captured console.log / console.error text for a specific invocation:

npx wawesome logs --invocation 019fb344-ea0c-78f2-8a9b-d04e188b9823

Or pass the UUID directly as the target:

npx wawesome logs 019fb344-ea0c-78f2-8a9b-d04e188b9823

3. Follow Live Output (--follow)

Stream an invocation's output as it runs — like tail -f for your serverless function.

Follow by function name (recommended)

npx wawesome logs my-function --follow

If the function is currently running, its output is streamed immediately. If the latest invocation already finished, the CLI waits for the next invocation to start and then streams it live. Press Ctrl-C at any time to stop.

Follow a specific invocation by ID

npx wawesome logs 019fb344-ea0c-78f2-8a9b-d04e188b9823 --follow

Reconnection

On transient network errors or server issues (5xx), the CLI automatically reconnects with exponential back-off (up to 3 retries). Non-recoverable errors like authentication failures (401) or unknown invocations (404) exit immediately with a clear message.


⚡ Manual Invocation

Fire a background run of a Function immediately without waiting for a schedule tick or deploying code:

# Invoke the function in the current directory and follow its output
npx wawesome invoke

# Invoke a specific function by name
npx wawesome invoke my-function

# Send custom HTTP method and request body
npx wawesome invoke -m POST -d '{"event":"audit"}'

# Fire without following live output
npx wawesome invoke --no-follow

⏰ Schedules & Cron Management

Manage recurring Schedules and inspect background run history directly from your terminal.

1. List Schedules

List a Function's Schedules with expression, state, and next run in UTC:

# List schedules for the function in the current directory
npx wawesome cron

# List schedules for a specific function
npx wawesome cron list my-function

# List schedules across all functions in an App
npx wawesome cron list --app my-app

The output clearly distinguishes the three off-states:

  • paused: stopped by a user, resumable with wawesome cron resume <name>
  • not in this config file: disabled because it was removed from configuration, resumable only by declaring it again in code
  • suspended: suspended by the non-payment ladder, resumable only after settling workspace balance

2. Pause and Resume Schedules

# Pause a schedule by name (stops queued ticks and survives future deploys)
npx wawesome cron pause nightly-reconcile

# Pause with an optional reason for incident context
npx wawesome cron pause nightly-reconcile --reason "database maintenance"

# Resume a paused schedule (recomputes next run from now, no catch-up backfilling)
npx wawesome cron resume nightly-reconcile

3. Read Run History

Inspect past scheduled and manual runs, showing when each run was due, when it started, pool delay, and how it ended:

# View run history for the current function
npx wawesome cron history

# Filter run history by state (pending, running, dispatched, skipped, missed, cancelled, lost, failed)
npx wawesome cron history my-function --state failed

⚙️ Configuration & Custom Gateway

wawesome-function.json

Every project directory includes a wawesome-function.json file generated during npx wawesome init:

{
  "app": "my-app",
  "function": "hello-world",
  "entry": "src/index.ts"
}

app is the App this Function is deployed into, and it is client-facing — every deploy from this directory is scoped to it.

function is the address. Changing it does not rename anything: your Function's URL is built from its name, so the next deploy lands on a Function of its own and the old one stays live at the old URL, serving the code its callers already hold. The CLI remembers where this directory last deployed and asks before that happens, naming both URLs. If you meant it, delete the old Function from the dashboard once nothing calls it.

Add "assets" to deploy static files beside your code:

{
  "app": "my-app",
  "function": "hello-world",
  "entry": "src/index.ts",
  "assets": "dist/client"
}

Everything under that directory is deployed with the version and served at its path beneath your Function's URL — dist/client/assets/index-a1.js answers at https://<app-host>/<function>/assets/index-a1.js. The CLI hashes each file and asks the platform which of them it does not already hold, so a redeploy that changed one chunk uploads one chunk — and a deploy that changed nothing at all is refused before a byte moves.

Files are served straight from object storage; your Function is never invoked for one, and no invocation is recorded. They answer on your App's own hostname and nowhere else — on the development path form (/x/<tenant>/<app>/<function>/...) the same address reaches your handler as it always has, because a file on an origin every workspace shares would be same-origin with all of them. Each carries Cache-Control: public, max-age=31536000, immutable and an ETag, so name your build output by content hash — a file's bytes must never change under a name a browser has already cached for a year. The content type comes from the extension against a fixed allowlist and is never sniffed; anything off it is served as a download.

Two rules to know about:

  • Everything beneath assets/ is static, whatever the deploy carries. A request there never reaches your handler — an unknown path under it is a 404, not a route for you to answer.
  • At most 100 files may sit outside assets/. Those paths travel on the version record so a request can be routed without a lookup per file. Put bulk output under assets/, where a file costs nothing; favicon.ico, robots.txt and a .well-known/ directory are what the rest is for.

HTML is refused at deploy time. Your Function renders its own markup, and a document served from your App's own origin is the sharpest same-origin vector a static file has.

An SVG is served with script denied. The rule behind the refusal above is that nothing you deploy as a file runs script on your App's own origin, and an SVG opened directly in a browser would. It is served rather than refused because denying it costs the file nothing: an <img src="logo.svg"> never ran that script, so your drawings render as they always did — every SVG and XML file carries Content-Security-Policy: script-src 'none', and only navigating straight to one loses anything.

Schedules

Add "schedules" to run a Function on a recurring timer, with no caller:

{
  "app": "my-app",
  "function": "nightly-reconcile",
  "entry": "src/index.ts",
  "schedules": [{ "name": "overnight", "expression": "0 3 * * *" }]
}

An expression is five fields, read in UTC — minute, hour, day of month, month, day of week. There is no seconds field, and no timezone: a local zone would make one night a year fire a job twice and another night not at all.

The name is yours to choose and is what the platform keys the schedule by, so editing an expression is a change to the same schedule rather than the deletion of one and the creation of another — its history and its paused state stay attached to it.

Your deploy applies them and prints each one with the time it will next run. It is refused, before anything is built or uploaded, if an expression cannot be read, if it would run more often than every five minutes, or if one Function declares more than five schedules.

Two rules worth knowing before you edit the file:

  • A schedule you delete from the file is disabled, not deleted. Its history stays, and declaring it again is what turns it back on — so a typo costs you a deploy rather than a job's record.
  • A deploy never resumes a schedule a person paused. When it runs is code; whether it is running is not, and an unrelated commit the next morning must not restart what you stopped at 3am.

Leaving schedules out of the file entirely says nothing about them and changes nothing. Writing "schedules": [] says this Function declares none, which disables the ones it used to have.

Keeping a Function off the web

A job on a timer should not also be sitting at a guessable URL where a stranger can fire it. Add "visibility" to make a Function unreachable from the internet:

{
  "app": "my-app",
  "function": "nightly-reconcile",
  "entry": "src/index.ts",
  "visibility": "private"
}

A private Function has no public address at all in production — not a hidden one, not one behind a credential. A request for it against your App's own hostname is answered with the same 404 as a Function that was never deployed. This is how you deploy a job with side effects without leaving it where a stranger who guesses the slug can fire it.

Leave the line out and your Function is public, which is what every Function without it has always been.

On your own machine, the local development surface serves Functions regardless of visibility, and honours an explicit x-wawesome-trigger header so you can exercise a scheduled run by hand:

# Exercise a private or scheduled function locally on the development path form:
curl -X POST http://localhost:3000/x/my-tenant-slug/default-app/nightly-reconcile \
  -H "x-wawesome-trigger: schedule"

Production strips the reserved header namespace inbound, so the same header against your App's own hostname reaches nothing that reads it: the run is a caller's, and a private Function is a 404 either way. Skipping your own authorization for a schedule run therefore opens nothing.

Making a Function private takes nothing but the deploy. Making it public again does not: deleting the line is refused, and the deploy tells you so having written nothing.

npx wawesome deploy --publish

That is deliberate — the line that keeps a job off the internet is one line, and a deploy that quietly honoured its deletion would put the job back on the open internet with nothing said. Every deploy prints the visibility it landed, beside the URL or in place of it.

Reserved headers

x-wawesome-* belongs to the platform in both directions. It is stripped off the request before your handler sees it, and off your response before the caller does — so do not name a header of your own on that prefix: it is dropped silently rather than rejected, and you will not get an error telling you why it vanished.

Four headers arrive or leave on it, and the stripping is what makes them worth trusting:

| Header | Direction | What it means | | --- | --- | --- | | x-wawesome-forwarded-prefix | inbound | The mount that was stripped from the path. Join it to the path you observe to rebuild the caller's URL. | | x-wawesome-trigger | inbound | How this run started: caller when someone called your address, schedule when fired by a Schedule. A caller cannot forge it in production (stripped inbound). On the local development surface, pass x-wawesome-trigger: schedule to exercise a background run by hand with the collapsed budget. | | x-wawesome-invocation-id | outbound | The id of this run — the key to fetch its logs with npx wawesome logs --invocation <id>. | | x-wawesome-error | outbound | Present only when the platform failed, never when your Function did. Its absence means the status on the wire is yours — up to the moment your response is committed, and no further. |

Testing against the guest's JavaScript surface

Your Function does not run on Node. The engine has no Intl, and its toLocaleString ignores the locale you pass — (1234.5).toLocaleString('en-US') comes back as "1234.5", not "1,234.50". On Node both work, which is how a green suite ships a Function that throws in production, or renders markup the browser then refuses to hydrate.

Point your test suite at the guest's surface instead:

// vitest.config.ts
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: { setupFiles: ["wawesome/vitest-setup"] },
});

Templates scaffolded with wawesome init --template ship this already. With it in place Intl is gone, MessageChannel is the platform's own implementation rather than Node's, and the locale-sensitive methods throw with a message naming the remedy — they throw rather than return the engine's unlocalised answer because the platform declares them unsupported, and a wrong string that fails nowhere is the thing this is here to stop you shipping.

If you bundle an Intl polyfill, declare it in your package.json as you normally would — a dependency that provides Intl is left in place rather than stripped out from under you.

Local Development / Gateway Overrides

If you are running a local gateway or self-hosted instance, you can configure your CLI Gateway URL using any of the following:

1. Custom Settings (~/.wawesome/settings.json)

Create ~/.wawesome/settings.json:

{
  "gateway_url": "http://localhost:3000"
}

2. Environment Variables

export WAWESOME_GATEWAY_URL="http://localhost:3000"

3. CLI Flag

npx wawesome login --gateway http://localhost:3000

🔒 Security & Secrets

Wawesome encrypts environment variables at rest using two-tier envelope encryption (AES-256-GCM with per-app data keys and AAD context binding). Use --secret when setting sensitive keys:

npx wawesome env set STRIPE_SECRET_KEY sk_live_xxx --secret

🌐 Resources & Support