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

@gyankumar62065/eum-social-local-emulator

v0.1.5

Published

Local development emulator for AWS End User Messaging Social

Readme

EUM Social Local Emulator

A local development emulator for AWS End User Messaging Social (WhatsApp). Run it on your workstation or in a development container, then point an AWS SDK client at its endpoint to develop and exercise application integrations without sending messages through AWS or Meta.

The emulator implements a documented subset of the AWS socialmessaging API using the service model included in this package. It provides seeded WhatsApp Business Accounts (WABAs), phone numbers and templates; a browser inbox; configurable message status flows and failure rules; and locally persisted messages and events. Event delivery can be connected to SNS-compatible services such as LocalStack.

This is a development tool. It does not connect to AWS End User Messaging or Meta, deliver real WhatsApp messages, verify provider credentials, or reproduce every AWS operation. Unsupported modeled operations return a modeled 501 response. Do not use it as a production message transport or with production credentials/data.

Who this is for

Use the emulator when an application uses the AWS SDK for End User Messaging Social and needs a repeatable local endpoint for development, demos, and integration checks. Keep your application's AWS SDK integration; configure its endpoint override to the emulator. The emulator is a separate local service, not an in-process replacement AWS SDK or a client-side library.

Requirements

  • Node.js 24 or newer to run the server (uses Node's built-in SQLite support)
  • Bun 1.3.12 or newer to install dependencies and run project scripts
  • Docker Compose only when using the optional LocalStack example

Quick start from this repository

bun install --frozen-lockfile
bun run ui:build
cp eum-social-local-emulator.example.yaml eum-social-local-emulator.yaml
EUM_CONFIG=./eum-social-local-emulator.yaml bun run start

The server listens on http://localhost:4582. Open the inbox at http://localhost:4582. On startup, the terminal prints seeded AWS resource IDs; use those IDs in your application calls. The default region is ap-south-1; local credentials such as test / test are accepted because the emulator does not authenticate AWS requests.

The database defaults to ./data/eum-local.db and is created automatically. The example config contains a WABA, phone number, and approved template. Copy it to eum-social-local-emulator.yaml before editing; that local file is intentionally not part of the published package.

Install the public package

The package is configured for public npm publication under the @gyankumar62065 scope. Once published, anyone can install it from the registry; a paid npm account is not required for public packages. This project uses Bun for installation and development scripts.

bun add @gyankumar62065/eum-social-local-emulator

Install it as a development dependency in your application or a dedicated tools package. Start the emulator from a directory containing eum-social-local-emulator.yaml:

bun add --dev @gyankumar62065/eum-social-local-emulator
cp node_modules/@gyankumar62065/eum-social-local-emulator/eum-social-local-emulator.example.yaml ./eum-social-local-emulator.yaml
EUM_CONFIG=./eum-social-local-emulator.yaml bunx eum-social-local-emulator

The server reads configuration from the current working directory by default, so set EUM_CONFIG when the config is elsewhere.

Connect an application

Configure the AWS SDK client in your application to use the emulator URL. Keep this endpoint override in local or test configuration; do not use it in production.

JavaScript / TypeScript (AWS SDK v3)

import { SocialMessagingClient } from '@aws-sdk/client-socialmessaging'

const socialMessaging = new SocialMessagingClient({
  region: 'ap-south-1',
  endpoint: process.env.EUM_ENDPOINT ?? 'http://localhost:4582',
  credentials: {
    accessKeyId: 'test',
    secretAccessKey: 'test',
  },
})

Use the generated resource IDs printed at emulator startup with the SDK commands your workflow needs. The emulator supports a subset of the modeled operations; check the API support notes below before depending on a particular operation.

Java (AWS SDK v2)

A small Java smoke-test project is included in the source repository under test/java:

cd test/java
mvn compile exec:java -Dexec.args="http://localhost:4582 PHONE_NUMBER_ID"

It exercises phone-number lookup, template sending, and modeled error handling. Replace PHONE_NUMBER_ID with the value printed by the running emulator.

Configuration

The emulator reads eum-social-local-emulator.yaml from the current working directory. Set EUM_CONFIG to select another file. If no file exists, it starts with defaults and no seeded WABAs.

| YAML key | Default | Purpose | | --- | --- | --- | | port | 4582 | HTTP port | | host | 127.0.0.1 | Bind address; loopback keeps the local admin API off the network by default | | dbPath | ./data/eum-local.db | SQLite database path | | region | ap-south-1 | Region used in generated ARNs and SDK integration | | accountId | 000000000000 | Account ID used in generated ARNs | | aws.endpoint | unset | Optional AWS-compatible endpoint for SNS events and S3 media; unset disables those integrations | | webhookUrl | unset | Optional webhook event destination | | wabas | [] | WABA seeds, including name, numeric metaWabaId, eventDestinations, phoneNumbers and templates | | templates.autoApproveSeconds | 0 | 0 approves immediately; -1 leaves templates for manual review in the inbox | | sim.defaultFlow | [sent, delivered, read] | Default status progression for simulated messages | | sim.stepDelayMs | 1000 | Delay between status events |

The default bind address is loopback, so the emulator and its administration API are available only from the local machine. To connect from another development container, set EUM_HOST=0.0.0.0 or configure host explicitly; this exposes the unauthenticated local administration API to that network, so use it only on a trusted development network. | sim.rules | [] | Ordered recipient/type/template rules for delivery status flows or immediate modeled request errors (denied, throttled, dependency, internal) | | messageIdMode | uuid | uuid or WhatsApp-style wamid message identifiers |

Environment variables override corresponding YAML settings:

| Variable | Purpose | | --- | --- | | EUM_CONFIG | Configuration file path | | EUM_PORT | HTTP port | | EUM_HOST | Bind address | | EUM_DB | SQLite database path | | EUM_REGION | AWS region | | EUM_AWS_ENDPOINT | AWS-compatible endpoint for SNS and S3 integrations |

The metaWabaId and metaPhoneNumberId values in seeds must be numeric strings. Seed phone numbers use E.164 format. See eum-social-local-emulator.example.yaml for a complete configuration example and inline rule examples.

Supported AWS operations

The current implementation simulates these AWS End User Messaging Social operations:

  • ListLinkedWhatsAppBusinessAccounts
  • GetLinkedWhatsAppBusinessAccount
  • GetLinkedWhatsAppBusinessAccountPhoneNumber
  • PutWhatsAppBusinessAccountEventDestinations
  • CreateWhatsAppMessageTemplate
  • CreateWhatsAppMessageTemplateMedia
  • UpdateWhatsAppMessageTemplate
  • GetWhatsAppMessageTemplate
  • ListWhatsAppMessageTemplates
  • SendWhatsAppMessage
  • PostWhatsAppMessageMedia
  • GetWhatsAppMessageMedia
  • DeleteWhatsAppMessageMedia

Other operations present in the vendored service model return a modeled 501 response. The emulator does not claim complete AWS service coverage. Synchronous invalid requests return modeled errors; simulation rules can also return immediate denied/throttled/dependency/internal failures. An unknown or inactive template is rejected synchronously. A structurally valid send whose Meta template parameter count is wrong can still be accepted by the AWS API simulation and then produce a Meta-style failed status event, preserving the distinction between API rejection and later provider delivery failure.

WhatsApp template document headers

Document-header templates use two distinct media records:

  1. Upload the approval sample with CreateWhatsAppMessageTemplateMedia, then include its metaHeaderHandle as the single HEADER component's example.header_handle value when creating or updating the template. The handle must belong to the same WABA and use a MIME type compatible with the header format.
  2. After approval, upload the actual recipient attachment with PostWhatsAppMessageMedia using the sending phone number. Send that returned mediaId in a single header document parameter. The emulator checks that the media exists, is owned by that phone number, and is compatible with the approved header before accepting the send.

The local inbox shows the actual send-time attachment, filename, MIME type, size and download control. Downloads return the stored bytes. The message inspector records the template generation, media ID, validation result, SHA-256 and size. Removing the stored media makes the inbox display an unavailable state; it never falls back to the approval sample.

| Media type | MIME types | Maximum size | | --- | --- | ---: | | Documents | text/plain, application/pdf, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-powerpoint, application/vnd.openxmlformats-officedocument.presentationml.presentation | 100 MB | | Images | JPEG, PNG | 5 MB | | Audio | audio/aac, audio/amr, audio/mpeg, audio/mp4, audio/ogg | 16 MB | | Video | video/mp4, video/3gp | 16 MB | | Static WebP sticker | WebP | 100 KB | | Animated WebP sticker | WebP | 500 KB |

Limits use decimal bytes as documented by AWS (for example, 100 MB is 100,000,000 bytes). Uploads are checked using their actual byte count. KAI may impose its own stricter aggregate attachment limits; those application rules are outside the emulator.

Malformed/missing headers and incompatible MIME types return the modeled InvalidParametersException (HTTP 400). Unknown or media owned by another sending phone returns ResourceNotFoundException (HTTP 404). These are the emulator's selected AWS API error mappings; the provider API references enumerate the operation error types but do not specify a unique mapping for each local media-validation case. Rejected sends do not create messages or delivery/read events. The emulator still simulates template approval, status callbacks, and inbound replies locally; none of these results establish real Meta approval or WhatsApp delivery.

Provider references: AWS supported media types and sizes, CreateWhatsAppMessageTemplateMedia, PostWhatsAppMessageMedia, and SendWhatsAppMessage.

Generic SDK walkthrough

This example discovers the WABA through the SDK, creates a template, approves that template using the local admin API, sends with the AWS SDK, then inspects the event log. Replace the phone number ID with a seeded ID printed at startup. The biz_opaque_callback_data value is copied into the corresponding status event. Set templates.autoApproveSeconds: -1 in the config if you want to review the template manually; the default configuration approves it automatically.

import {
  CreateWhatsAppMessageTemplateCommand,
  ListLinkedWhatsAppBusinessAccountsCommand,
  SendWhatsAppMessageCommand,
  SocialMessagingClient,
} from '@aws-sdk/client-socialmessaging'

const endpoint = process.env.EUM_ENDPOINT ?? 'http://localhost:4582'
const client = new SocialMessagingClient({
  region: 'ap-south-1', endpoint,
  credentials: { accessKeyId: 'test', secretAccessKey: 'test' },
})

const { linkedAccounts = [] } = await client.send(new ListLinkedWhatsAppBusinessAccountsCommand({}))
const waba = linkedAccounts[0]
if (!waba) throw new Error('No WABA is seeded; add one in the emulator configuration.')

const definition = new TextEncoder().encode(JSON.stringify({
  name: 'local_order_update', language: 'en_US', category: 'UTILITY',
  parameter_format: 'POSITIONAL',
  components: [{ type: 'BODY', text: 'Order {{1}} is ready.' }],
}))
const created = await client.send(new CreateWhatsAppMessageTemplateCommand({ id: waba.id, templateDefinition: definition }))
const approval = await fetch(`${endpoint}/_eum/api/templates/${created.metaTemplateId}/approve`, {
  method: 'POST', headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ generation: 1 }),
})
if (!approval.ok) throw new Error(await approval.text())

const sent = await client.send(new SendWhatsAppMessageCommand({
  originationPhoneNumberId: process.env.EUM_PHONE_NUMBER_ID!, metaApiVersion: 'v20.0',
  message: new TextEncoder().encode(JSON.stringify({
    messaging_product: 'whatsapp', to: '+15550000001', type: 'template',
    biz_opaque_callback_data: 'order-123',
    template: { name: 'local_order_update', language: { code: 'en_US' }, components: [
      { type: 'body', parameters: [{ type: 'text', text: 'ORDER-123' }] },
    ] },
  })),
}))
console.log('AWS message ID:', sent.messageId)
const { messages } = await (await fetch(`${endpoint}/_eum/api/messages`)).json()
const outbound = messages.find((message: { awsMessageId?: string }) => message.awsMessageId === sent.messageId)
if (!outbound) throw new Error('The sent message was not found in the local inbox.')
await new Promise((resolve) => setTimeout(resolve, 3500)) // default flow emits sent, delivered, read over 3 seconds
const detail = await (await fetch(`${endpoint}/_eum/api/messages/${encodeURIComponent(outbound.wamid)}`)).json()
console.log('Meta WAMID and status events:', outbound.wamid, detail.history, detail.events)
const reply = await fetch(`${endpoint}/_eum/api/inbound`, {
  method: 'POST', headers: { 'content-type': 'application/json' },
  body: JSON.stringify({
    phoneNumberId: process.env.EUM_PHONE_NUMBER_ID, from: '+15550000001', type: 'interactive',
    interactiveType: 'button_reply', id: 'confirm', title: 'Confirm', contextMessageId: outbound.wamid,
  }),
})
if (!reply.ok) throw new Error(await reply.text())

For a correlated customer reply, use the inbox composer after selecting an outbound message and enable Quote selected. The generic admin equivalent is:

{
  "phoneNumberId": "PHONE_NUMBER_ID",
  "from": "+15550000001",
  "type": "interactive",
  "interactiveType": "button_reply",
  "id": "confirm",
  "title": "Confirm",
  "contextMessageId": "wamid.ORIGINAL_OUTBOUND_ID"
}

The inbound webhook contains its own message ID and context.id equal to the supplied Meta WAMID. The emulator checks that the referenced outbound message belongs to the same phone and customer. Native button replies preserve text and payload separately; list_reply also supports an optional description. URL buttons are presented as links and do not create inbound quick replies.

Manual template decisions use POST /_eum/api/templates/:id/{approve|reject|pause|disable} with a JSON body such as {"generation": 2, "expectedStatus": "PENDING", "reason": "INVALID_FORMAT"}. The expected status prevents stale UI actions from overriding a newer decision. Event destinations with a failed delivery can be retried using POST /_eum/api/events/:eventId/replay and {"destination": "arn:aws:sns:..."}. Replay resends the stored EUM event; it does not send the WhatsApp message again.

HTTP endpoints

| Path | Purpose | | --- | --- | | /v1/... | AWS End User Messaging Social API endpoint used by SDK clients | | /_eum/health | Health check | | /_eum/ui/ | Browser inbox for conversations, templates, and event inspection | | /_eum/api/... | Emulator administration API used by the inbox |

Messages, simulated status changes, customer replies, and events are stored in the local database. When configured, event envelopes are also sent to the configured SNS-compatible destination or webhook. A delivery error is recorded locally and does not turn a successful simulated send into an AWS API failure.

LocalStack event integration

The example Compose file starts LocalStack and the emulator, then provisions an SNS topic and SQS subscription for inspecting events. This file is in the source repository and is not included in the npm package.

docker compose -f docker-compose.example.yml up --build

The inbox is available at http://localhost:4582. The LocalStack endpoint is http://localhost:4566. To run the opt-in SNS-to-SQS integration check when LocalStack is running:

bun run test:e2e

Set EUM_E2E_LOCALSTACK to use a different LocalStack endpoint. This check is intended for the source repository and is not included in the published runtime package.

Development commands

These commands are for a clone of the source repository, not an installed npm package.

bun install --frozen-lockfile
bun run ui:dev       # UI development server
bun run ui:build     # Build UI assets
bun run dev          # Run server with Node watch mode
bun run start        # Run server
bun run typecheck
bun run test

To refresh the vendored API model when updating against a newer AWS service model:

bun run models:update

Review the model source, generated operation behavior, and this README's support notes before releasing an updated emulator. The model update script requires network access to the upstream AWS model source.

Package maintenance and public publishing

The package is scoped as @gyankumar62065/eum-social-local-emulator and configured for public registry access. Authenticate to npm as the owner of the scope, then from the repository root:

bun install --frozen-lockfile
bun publish --access public

The package's prepack script builds the inbox UI; the allowlist in package.json includes the command, runtime source, service model, built UI, example configuration, and README. It excludes local configuration, databases, tests, and development-only files. Review the package contents and version before each publish. The npm package is independent of the GitHub repository's visibility; the source repository is currently public as well.

Design

The original architecture is described in the initial design. The current generic compatibility and lifecycle scope is in the completeness specification, with its implementation breakdown in the implementation plan.

License

This project is licensed under the MIT License.