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

@imessage-sdk/chat-adapter

v0.1.0

Published

Provider-neutral iMessage adapter for Chat SDK

Readme

@imessage-sdk/chat-adapter

Provider-neutral iMessage adapter for Chat SDK, powered by imessage-sdk.

Install

Install the adapter, Chat SDK, core SDK, state backend, and one provider:

pnpm add @imessage-sdk/chat-adapter @imessage-sdk/blooio imessage-sdk chat @chat-adapter/state-pg

Usage

import { createPostgresState } from '@chat-adapter/state-pg';
import { Chat } from 'chat';

import { blooio } from '@imessage-sdk/blooio';
import { createIMessageAdapter } from '@imessage-sdk/chat-adapter';

const imessage = createIMessageAdapter({
  provider: blooio(),
});

export const chat = new Chat({
  userName: 'my-agent',
  adapters: { imessage },
  state: createPostgresState({
    url: process.env.DATABASE_URL,
  }),
});

chat.onDirectMessage(async (thread, message) => {
  await thread.post(`You said: ${message.text}`);
});

The provider owns credentials and transport configuration. For Blooio, configure BLOOIO_API_KEY, BLOOIO_FROM_NUMBER, and BLOOIO_WEBHOOK_SECRET, or pass the equivalent values to blooio().

The concrete provider remains available with its full type information:

imessage.client.provider;
// "blooio"

imessage.client.providers.blooio.numbers.list();

Hono webhook

import { waitUntil } from '@vercel/functions';
import { Hono } from 'hono';

import { chat } from './chat.js';

const app = new Hono().post('/webhooks/imessage', (context) =>
  chat.webhooks.imessage(context.req.raw, { waitUntil }),
);

export default app;

Point the selected provider's signed webhook at POST /webhooks/imessage.

Direct messages

Open a conversation from a phone number or email address:

const threadId = await imessage.openDM('+15551234567');
await chat.thread(threadId).post('Hello from Chat SDK');

Prefix an address when its kind would otherwise be ambiguous:

await imessage.openDM('phone:+15551234567');
await imessage.openDM('email:[email protected]');

Thread IDs are versioned and contain the provider, connection ID, and provider-native conversation ID:

imessage:v1:<base64url-json>

An adapter instance rejects thread IDs belonging to another provider connection.

Attachments and formatting

Chat SDK Markdown and AST messages are rendered as plain text. Outbound Chat SDK attachment URLs, Blobs, Buffers, and ArrayBuffers are converted into normalized SDK attachments. The selected provider may reject source types that it cannot transport; public URLs are the currently verified cross-provider path.

Inbound public attachments, including Blooio attachments, expose url. Providers with authenticated attachment downloads expose Chat SDK's lazy fetchData() instead. Photon downloads the primary attachment bytes using the provider connection:

const [attachment] = message.attachments;
const data = await attachment?.fetchData?.();

The adapter also stores provider, connection, and attachment IDs in fetchMetadata, allowing Chat SDK to reconstruct fetchData after queue or state serialization.

await chat.thread(threadId).post({
  markdown: '**Photo**',
  attachments: [
    {
      type: 'image',
      url: 'https://example.com/photo.jpg',
      name: 'photo.jpg',
      mimeType: 'image/jpeg',
    },
  ],
});

Reactions, typing, and read state

Reactions are limited to iMessage tapbacks: love, like, dislike, laugh, emphasize, and question. Common Chat SDK aliases such as heart, thumbs_up, and thumbs_down are mapped automatically.

await imessage.addReaction(threadId, messageId, 'heart');
await imessage.startTyping(threadId);
await imessage.markRead(threadId);

The adapter tracks conversations started through thread.startTyping(). Posting a message stops typing in a finally block, including when sending fails, and disconnecting clears any remaining indicators before closing the provider. This prevents persistent Blooio and Photon indicators from remaining active after a response.

Operations are checked against the selected provider's runtime capabilities. Chat SDK NotImplementedError is thrown for unavailable normalized operations.

Echo prevention and history

The adapter stores exact outbound provider message IDs in the configured Chat SDK state adapter for 24 hours. This prevents provider webhook echoes from being processed as new user messages and works across serverless instances when a persistent state adapter is used.

Set persistThreadHistory = true so Chat SDK stores conversation history in its state adapter. The adapter's direct fetchMessages() method maintains only a bounded in-process cache because imessage-sdk does not yet expose provider-neutral message-history pagination.

v0.1 scope

Supported:

  • One provider connection per adapter instance.
  • Direct conversations.
  • Inbound and outbound text.
  • Attachments supported by the selected provider.
  • Signed webhook verification and normalized inbound events.
  • Tapback reactions.
  • Typing indicators.
  • Message lookup, thread metadata, and read state where supported.
  • Typed access to the normalized client and concrete provider.

Postponed:

  • Group conversations.
  • Persistent provider event streams.
  • Mentions, cards, modals, and ephemeral messages.
  • Native Chat SDK response streaming.
  • Provider-neutral history pagination.
  • Editing and deletion for the current built-in providers.

Photon and other providers use the same adapter factory:

import { createIMessageAdapter } from '@imessage-sdk/chat-adapter';
import { photon } from '@imessage-sdk/photon';

const imessage = createIMessageAdapter({
  provider: photon(),
});