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

@withwiz/sms-core

v0.1.0

Published

SMS/LMS/MMS sending core with a provider-neutral interface and Aligo/Solapi adapters

Readme

@withwiz/sms-core

English | 한국어

SMS/LMS/MMS sending core. Provides a provider-neutral interface with Aligo and Solapi adapters.

Install

pnpm add @withwiz/sms-core
# only when using Solapi
pnpm add solapi

Usage

Provider adapters are imported from a subpath, not from the root entry point. The Aligo adapter depends on Node's fetch and Blob, and the Solapi adapter depends on an SDK that uses fs. If they were exported from the root as well, server-only code would end up in the browser bundle even when a client component imports nothing but a general-purpose helper such as a phone number utility.

import { createAligoProvider } from '@withwiz/sms-core/providers/aligo';

const provider = createAligoProvider({
  credentials: async () => ({ userId, apiKey, sender }),
});

const outcome = await provider.send({
  type: 'SMS',
  sender,
  content: 'This is a notification message.',
  recipients: ['01012345678', '01087654321'],
});

if (!outcome.ok) {
  console.error(outcome.errorCode, outcome.errorMessage);
}

The message type (type) is decided by the caller. When the user picks SMS/LMS/MMS directly, as with the tabs of a send form, no separate type resolution logic is needed.

Helpers

  • byteLength: counts the byte length on an EUC-KR basis. The send form in @withwiz/sms-admin uses it to display the byte count of the body and subject as they are being typed.
  • resolveMessageType: a helper for deciding the message type automatically from the content and the attachment state. Like chunkRecipients, it currently has no consumer.

Request validation

send checks the recipient list before it loads credentials or touches the network. An empty list, a number that is not a valid mobile number, and a list longer than the provider accepts are all rejected with an SmsProviderError. Until this check existed, capabilities.maxRecipientsPerRequest announced a limit that nothing enforced, and isValidMobilePhone described itself as the source of truth for recipient validation while no send path called it.

An invalid entry rejects the whole request rather than being filtered out, because a caller that is not told about the omission would believe every recipient was reached. To drop bad entries instead, filter with isValidMobilePhone before calling send; to send to more recipients than one request allows, split the list with chunkRecipients.

The error names the positions of the offending entries rather than the numbers themselves, since an error message travels into logs and a phone number is personal data.

The sender is checked as well, once the credentials have supplied the fallback for a request that omits one. A sender follows wider rules than a recipient, since it is often a landline or a representative number rather than a mobile number, so it has its own check: a number starting with 0 that is 9 to 11 digits long, or an 8-digit representative number in the 15xx, 16xx and 18xx ranges. isValidSenderPhone is exported for use in a form. Only the shape is checked — providers send only from numbers registered to the account in advance, and nothing local can confirm that, so a number accepted here may still be rejected by the provider.

MMS image safety

An MMS imageUrl is supplied by the caller, and the server is what fetches it. Both adapters therefore validate the URL before anything acts on it: only http: and https: are accepted, and loopback, private, link-local and other reserved ranges are refused, which covers the cloud metadata endpoint at 169.254.169.254. The Aligo adapter, which fetches the image itself, additionally refuses to follow redirects, applies a timeout, and abandons a body that outgrows the size limit while it is still arriving. The Solapi adapter validates the URL before handing it to the SDK, whose uploadFile would otherwise accept a local file path.

Name resolution happens after validation, so a host name that resolves to a blocked address is not caught. Restrict outbound traffic at the network level when that matters.

The rules live in src/config/image-fetch.ts as defaults. A deployment whose image server sits inside a private network overrides them per provider:

import {
  createAligoProvider,
  DEFAULT_IMAGE_FETCH_POLICY,
} from '@withwiz/sms-core/providers/aligo';

const provider = createAligoProvider({
  credentials: async () => ({ userId, apiKey, sender }),
  imageFetchPolicy: { ...DEFAULT_IMAGE_FETCH_POLICY, blockedSubnets: [] },
});

Supplying fetchImage replaces the built-in fetch entirely, including these checks, so such an implementation has to validate a caller-supplied URL itself.

Provider capability comparison

| Item | Aligo | Solapi | |------|-------|--------| | Recipients per request | 1,000 | 10,000 | | Per-recipient result | Not provided | Provided | | MMS image | Attached with each request | Uploaded in advance |

License

MIT