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

tg-mini-app-emulator

v0.1.1

Published

Local Telegram Mini App emulator — run mini-apps without ngrok or a real bot.

Readme

tg-mini-app-emulator

Run Telegram Mini Apps locally — no ngrok, no real bot, no public URL.

Prerequisites

  • Node.js ≥ 20 (CLI runs on plain Node; Bun not required)
  • A locally running mini-app dev server (any localhost port)

Quick start

# 1. Run your mini app dev server (any localhost port)
npm run dev   # e.g. http://localhost:5173

# 2. Run the emulator pointing at it
npx tg-mini-app-emulator --target http://localhost:5173

# 3. Open http://localhost:1234

In your mini-app HTML, load the emulator SDK (dev only):

<script src="http://localhost:1234/sdk.js"></script>

Use query ?emu=http://localhost:PORT on your mini-app URL when the emulator listens on a non-default port.

Production stays on https://telegram.org/js/telegram-web-app.js.

Forward sendData to your bot's webhook

npx tg-mini-app-emulator \
  --target http://localhost:5173 \
  --webhook http://localhost:3000/telegram/webhook \
  --webhook-secret your-secret \
  --bot-token 123456:ABC...

Bot token alignment: the emulator HMAC-signs initData with the bot token you pass. Your backend must verify with the same token, or it will reject the launch as invalid_init_data. In dev, prefer --bot-token-env ./path/to/.env to read TELEGRAM_BOT_TOKEN from a single source of truth.

Simulating real launch sources (channel inline button vs attachment menu)

Real Telegram delivers different initData shapes per entry point. The emulator simulates this via launchSource:

| launchSource | Real-world trigger | initData includes | Channel id source | | ------------------------- | ------------------------------------------ | ------------------------------------------------ | ------------------------------------------ | | attachment_menu (default) | 📎 menu inside chat | chat (id/type/title) | chat.id | | direct_link | Channel post inline button, t.me/bot/app | chat_type + chat_instance (no chat.id) | bot encodes channel_id into start_param|

direct_link requires chatType. chatInstance and queryId are optional.

Config file (multi-profile)

npx tg-mini-app-emulator --config examples/tg-emu.config.json --profile channel_inline

A complete runnable example ships with the package — see examples/tg-emu.config.json after install. Bundled profiles:

| Profile | Launch source | Notes | | ------------------------- | -------------------- | -------------------------------------------------------- | | channel_inline | direct_link | Demo channel via start_param=channel_id=DEMO_CH_001 | | attachment_menu_channel | attachment_menu | Channel chat with chat.id | | vip_direct_link | direct_link | Second demo identity for testing |

You may omit --target when the config file provides one.

Profile fields

| Field | Required | Description | | -------------- | ------------------ | ------------------------------------------------------------------ | | user | yes | TgUser object, at minimum id + first_name | | chat | yes | Used for webhook forward; not injected into initData when launchSource=direct_link | | launchSource | no | attachment_menu (default) / direct_link | | chatType | yes for direct_link| private / group / supergroup / channel / sender | | chatInstance | no | Stable hash representing source chat (direct_link scenarios) | | queryId | no | For answerWebAppQuery | | startParam | no | Maps to t.me/bot/app?startapp=...; recommended key=value format| | theme | no | light / dark |

CLI

| Flag | Required | Description | | ------------------------ | -------- | -------------------------------------------------------------------------------------------- | | --target <url> | yes* | Mini app dev URL (localhost / 127.0.0.1 / *.local) | | --webhook <url> | no | Forward sendData / callback as Telegram Update JSON | | --webhook-secret <s> | no | Sets x-telegram-bot-api-secret-token header | | --bot-token <t> | no | Bot token for HMAC initData signing | | --bot-token-env <path> | no | Read TELEGRAM_BOT_TOKEN from this .env file; explicit --bot-token takes precedence | | --port <n> | no | Emulator port (default 1234) | | --config <path> | no | JSON config path | | --profile <name> | no | Active profile | | --no-open | no | Do not open browser |

*Not required if --config provides target.

SDK coverage (B-tier)

  • ready / expand / close
  • initData / initDataUnsafe
  • MainButton, BackButton
  • themeParams / colorScheme + themeChanged
  • viewportHeight / viewportStableHeight / isExpanded + viewportChanged
  • HapticFeedback (log only in emulator)
  • sendData → webhook forward as web_app_data
  • onEvent / offEvent

Examples

The package ships a runnable minimal mini-app at examples/minimal-mini-app/ (after install: node_modules/tg-mini-app-emulator/examples/minimal-mini-app/). It exercises MainButton, BackButton, HapticFeedback, themeChanged, and start_param parsing.

To try it end-to-end:

# Terminal 1 — serve the example mini-app
npx serve node_modules/tg-mini-app-emulator/examples/minimal-mini-app -l 5173

# Terminal 2 — emulator pointing at it, with a profile
npx tg-mini-app-emulator \
  --config node_modules/tg-mini-app-emulator/examples/tg-emu.config.json \
  --profile channel_inline

Troubleshooting

iframe shows "session expired / please reopen from Telegram" (API responds 401)

The emulator-signed initData HMAC does not match the bot token your backend verifies with. Use --bot-token-env pointing to your backend's .env, or pass the same token explicitly via --bot-token.

Verify the cookie flow with curl (replace endpoint and cookie name as appropriate):

curl -i -X POST http://<your-backend>/entry \
  -H "content-type: application/json" \
  -d "{\"initData\":\"$(curl -s http://localhost:1234/api/init-data | jq -r .initData)\"}"
# Expected: 302 + Set-Cookie

MainButton / BackButton don't trigger your handler

Make sure the SDK script tag uses the emulator origin in dev (http://localhost:1234/sdk.js) instead of https://telegram.org/js/telegram-web-app.js. Production must use the real URL.

Not covered (future)

  • CloudStorage, BiometricManager
  • showPopup / showAlert / showConfirm
  • openInvoice / openLink
  • SettingsButton, SecondaryButton

License

MIT