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

@tagea/capacitor-matrix

v0.3.1

Published

A capacitor plugin wrapping native matrix SDKs

Downloads

370

Readme

@tremaze/capacitor-matrix

A Capacitor plugin that provides a unified API for the Matrix communication protocol across Web, Android, and iOS.

Features

  • Authentication — password login, token-based login, session persistence
  • Real-time sync — incremental sync with state change events
  • Rooms — create, join, leave, list rooms with summaries
  • Messaging — send/receive text, notices, emotes; paginated message history
  • Media — upload and send images, audio, video, and files; resolve mxc:// URLs
  • Reactions & Redactions — react to messages, delete (redact) events
  • Room management — rename rooms, set topics, invite/kick/ban/unban users
  • Typing indicators — send and receive typing notifications
  • Presence — set and query online/offline/unavailable status
  • End-to-end encryption — cross-signing, key backup, secret storage, recovery, room key export/import

Requirements

| Platform | Minimum Version | | -------- | --------------- | | Capacitor | 8.0.0 | | iOS | 16.0 | | Android | API 24 (Android 7.0) |

Install

npm install @tremaze/capacitor-matrix
npx cap sync

iOS

No additional setup required. The Swift Package Manager dependency on matrix-rust-components-swift is resolved automatically.

Android

The plugin uses matrix-rust-sdk via Maven. Ensure your project's build.gradle includes mavenCentral() in the repositories block (this is the default for new Capacitor projects).

Usage

Import

import { Matrix } from '@tremaze/capacitor-matrix';

Authentication

// Login with username and password
const session = await Matrix.login({
  homeserverUrl: 'https://matrix.example.com',
  userId: '@alice:example.com',
  password: 'secret',
});
console.log('Logged in as', session.userId);

// Or restore a session using a stored access token
const session = await Matrix.loginWithToken({
  homeserverUrl: 'https://matrix.example.com',
  accessToken: 'syt_...',
  userId: '@alice:example.com',
  deviceId: 'ABCDEF',
});

// Check for an existing session (persisted in localStorage on web)
const existing = await Matrix.getSession();
if (existing) {
  console.log('Already logged in as', existing.userId);
}

// Logout
await Matrix.logout();

Sync

Start the background sync loop to receive real-time updates:

// Listen for sync state changes
await Matrix.addListener('syncStateChange', ({ state, error }) => {
  console.log('Sync state:', state); // 'INITIAL' | 'SYNCING' | 'ERROR' | 'STOPPED'
  if (error) console.error('Sync error:', error);
});

await Matrix.startSync();

// Later, stop syncing
await Matrix.stopSync();

Rooms

// List joined rooms
const { rooms } = await Matrix.getRooms();
rooms.forEach((room) => {
  console.log(room.name, `(${room.memberCount} members)`);
});

// Create a room
const { roomId } = await Matrix.createRoom({
  name: 'Project Chat',
  topic: 'Discussion about the project',
  isEncrypted: true,
  invite: ['@bob:example.com'],
});

// Join a room by ID or alias
await Matrix.joinRoom({ roomIdOrAlias: '#general:example.com' });

// Leave a room
await Matrix.leaveRoom({ roomId: '!abc:example.com' });

// Get room members
const { members } = await Matrix.getRoomMembers({ roomId });

Messaging

// Send a text message
const { eventId } = await Matrix.sendMessage({
  roomId: '!abc:example.com',
  body: 'Hello, world!',
});

// Send a notice or emote
await Matrix.sendMessage({
  roomId,
  body: 'This is a notice',
  msgtype: 'm.notice',
});

// Load message history (paginated)
const { events, nextBatch } = await Matrix.getRoomMessages({
  roomId,
  limit: 50,
});

// Load older messages
const older = await Matrix.getRoomMessages({
  roomId,
  limit: 50,
  from: nextBatch,
});

// Listen for new messages in real time
await Matrix.addListener('messageReceived', ({ event }) => {
  console.log(`${event.senderId}: ${event.content.body}`);
});

Media

// Send an image (provide a file URI on native, or a blob URL on web)
await Matrix.sendMessage({
  roomId,
  body: 'photo.jpg',
  msgtype: 'm.image',
  fileUri: 'file:///path/to/photo.jpg',
  fileName: 'photo.jpg',
  mimeType: 'image/jpeg',
});

// Resolve an mxc:// URL to an HTTP URL for display
const { httpUrl } = await Matrix.getMediaUrl({
  mxcUrl: 'mxc://example.com/abc123',
});

Reactions & Redactions

// React to a message
await Matrix.sendReaction({
  roomId,
  eventId: '$someEvent',
  key: '👍',
});

// Redact (delete) a message
await Matrix.redactEvent({
  roomId,
  eventId: '$someEvent',
  reason: 'Sent by mistake',
});

Room Management

await Matrix.setRoomName({ roomId, name: 'New Room Name' });
await Matrix.setRoomTopic({ roomId, topic: 'Updated topic' });

await Matrix.inviteUser({ roomId, userId: '@carol:example.com' });
await Matrix.kickUser({ roomId, userId: '@dave:example.com', reason: 'Inactive' });
await Matrix.banUser({ roomId, userId: '@eve:example.com' });
await Matrix.unbanUser({ roomId, userId: '@eve:example.com' });

Typing Indicators

// Send a typing notification
await Matrix.sendTyping({ roomId, isTyping: true, timeout: 5000 });

// Stop typing
await Matrix.sendTyping({ roomId, isTyping: false });

// Listen for typing events
await Matrix.addListener('typingChanged', ({ roomId, userIds }) => {
  if (userIds.length > 0) {
    console.log(`${userIds.join(', ')} typing in ${roomId}`);
  }
});

Presence

// Set your presence
await Matrix.setPresence({ presence: 'online', statusMsg: 'Available' });

// Get another user's presence
const info = await Matrix.getPresence({ userId: '@bob:example.com' });
console.log(info.presence, info.statusMsg);

End-to-End Encryption

// Initialize the crypto module (call after login, before startSync)
await Matrix.initializeCrypto();

// Check encryption status
const status = await Matrix.getEncryptionStatus();
console.log('Cross-signing ready:', status.isCrossSigningReady);
console.log('Key backup enabled:', status.isKeyBackupEnabled);

// Bootstrap cross-signing (first-time device setup)
await Matrix.bootstrapCrossSigning();

// Set up server-side key backup
const backup = await Matrix.setupKeyBackup();

// Set up recovery (generates a recovery key)
const { recoveryKey } = await Matrix.setupRecovery();
console.log('Save this recovery key:', recoveryKey);

// Or use a passphrase-based recovery key
const recovery = await Matrix.setupRecovery({ passphrase: 'my secret phrase' });

// Recover on a new device
await Matrix.recoverAndSetup({ recoveryKey: 'EsXa ...' });
// or
await Matrix.recoverAndSetup({ passphrase: 'my secret phrase' });

// Export/import room keys (for manual backup)
const { data } = await Matrix.exportRoomKeys({ passphrase: 'backup-pass' });
await Matrix.importRoomKeys({ data, passphrase: 'backup-pass' });

Event Listeners

// Room updates (name change, new members, etc.)
await Matrix.addListener('roomUpdated', ({ roomId, summary }) => {
  console.log(`Room ${summary.name} updated`);
});

// Read receipt updates (fires when another user reads messages in a room)
await Matrix.addListener('receiptReceived', ({ roomId }) => {
  console.log(`New read receipt in ${roomId}`);
  // Refresh message statuses to update read indicators
});

// Clean up all listeners
await Matrix.removeAllListeners();

Read Markers

// Mark a room as read up to a specific event
await Matrix.markRoomAsRead({
  roomId: '!abc:example.com',
  eventId: '$latestEvent',
});

// Refresh read statuses for specific messages (useful after receiving a receiptReceived event)
const { events } = await Matrix.refreshEventStatuses({
  roomId: '!abc:example.com',
  eventIds: ['$event1', '$event2'],
});
events.forEach((evt) => {
  console.log(`${evt.eventId}: ${evt.status}`, evt.readBy);
});

API Reference

The full API reference is auto-generated below from the TypeScript definitions.

login(...)

login(options: LoginOptions) => Promise<SessionInfo>

| Param | Type | | ------------- | ----------------------------------------------------- | | options | LoginOptions |

Returns: Promise<SessionInfo>


loginWithToken(...)

loginWithToken(options: LoginWithTokenOptions) => Promise<SessionInfo>

| Param | Type | | ------------- | ----------------------------------------------------------------------- | | options | LoginWithTokenOptions |

Returns: Promise<SessionInfo>


logout()

logout() => Promise<void>

getSession()

getSession() => Promise<SessionInfo | null>

Returns: Promise<SessionInfo | null>


startSync()

startSync() => Promise<void>

stopSync()

stopSync() => Promise<void>

getSyncState()

getSyncState() => Promise<{ state: SyncState; }>

Returns: Promise<{ state: SyncState; }>


createRoom(...)

createRoom(options: { name?: string; topic?: string; isEncrypted?: boolean; isDirect?: boolean; invite?: string[]; preset?: 'private_chat' | 'trusted_private_chat' | 'public_chat'; historyVisibility?: 'invited' | 'joined' | 'shared' | 'world_readable'; }) => Promise<{ roomId: string; }>

| Param | Type | | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | options | { name?: string; topic?: string; isEncrypted?: boolean; isDirect?: boolean; invite?: string[]; preset?: 'private_chat' | 'trusted_private_chat' | 'public_chat'; historyVisibility?: 'invited' | 'joined' | 'shared' | 'world_readable'; } |

Returns: Promise<{ roomId: string; }>


getRooms()

getRooms() => Promise<{ rooms: RoomSummary[]; }>

Returns: Promise<{ rooms: RoomSummary[]; }>


getRoomMembers(...)

getRoomMembers(options: { roomId: string; }) => Promise<{ members: RoomMember[]; }>

| Param | Type | | ------------- | -------------------------------- | | options | { roomId: string; } |

Returns: Promise<{ members: RoomMember[]; }>


joinRoom(...)

joinRoom(options: { roomIdOrAlias: string; }) => Promise<{ roomId: string; }>

| Param | Type | | ------------- | --------------------------------------- | | options | { roomIdOrAlias: string; } |

Returns: Promise<{ roomId: string; }>


leaveRoom(...)

leaveRoom(options: { roomId: string; }) => Promise<void>

| Param | Type | | ------------- | -------------------------------- | | options | { roomId: string; } |


forgetRoom(...)

forgetRoom(options: { roomId: string; }) => Promise<void>

| Param | Type | | ------------- | -------------------------------- | | options | { roomId: string; } |


sendMessage(...)

sendMessage(options: SendMessageOptions) => Promise<{ eventId: string; }>

| Param | Type | | ------------- | ----------------------------------------------------------------- | | options | SendMessageOptions |

Returns: Promise<{ eventId: string; }>


editMessage(...)

editMessage(options: EditMessageOptions) => Promise<{ eventId: string; }>

| Param | Type | | ------------- | ----------------------------------------------------------------- | | options | EditMessageOptions |

Returns: Promise<{ eventId: string; }>


sendReply(...)

sendReply(options: SendReplyOptions) => Promise<{ eventId: string; }>

| Param | Type | | ------------- | ------------------------------------------------------------- | | options | SendReplyOptions |

Returns: Promise<{ eventId: string; }>


getRoomMessages(...)

getRoomMessages(options: { roomId: string; limit?: number; from?: string; }) => Promise<{ events: MatrixEvent[]; nextBatch?: string; }>

| Param | Type | | ------------- | --------------------------------------------------------------- | | options | { roomId: string; limit?: number; from?: string; } |

Returns: Promise<{ events: MatrixEvent[]; nextBatch?: string; }>


markRoomAsRead(...)

markRoomAsRead(options: { roomId: string; eventId: string; }) => Promise<void>

| Param | Type | | ------------- | ------------------------------------------------- | | options | { roomId: string; eventId: string; } |


refreshEventStatuses(...)

refreshEventStatuses(options: { roomId: string; eventIds: string[]; }) => Promise<{ events: MatrixEvent[]; }>

| Param | Type | | ------------- | ---------------------------------------------------- | | options | { roomId: string; eventIds: string[]; } |

Returns: Promise<{ events: MatrixEvent[]; }>


redactEvent(...)

redactEvent(options: { roomId: string; eventId: string; reason?: string; }) => Promise<void>

| Param | Type | | ------------- | ------------------------------------------------------------------ | | options | { roomId: string; eventId: string; reason?: string; } |


sendReaction(...)

sendReaction(options: { roomId: string; eventId: string; key: string; }) => Promise<{ eventId: string; }>

| Param | Type | | ------------- | -------------------------------------------------------------- | | options | { roomId: string; eventId: string; key: string; } |

Returns: Promise<{ eventId: string; }>


setRoomName(...)

setRoomName(options: { roomId: string; name: string; }) => Promise<void>

| Param | Type | | ------------- | ---------------------------------------------- | | options | { roomId: string; name: string; } |


setRoomTopic(...)

setRoomTopic(options: { roomId: string; topic: string; }) => Promise<void>

| Param | Type | | ------------- | ----------------------------------------------- | | options | { roomId: string; topic: string; } |


setRoomAvatar(...)

setRoomAvatar(options: { roomId: string; mxcUrl: string; }) => Promise<void>

| Param | Type | | ------------- | ------------------------------------------------ | | options | { roomId: string; mxcUrl: string; } |


inviteUser(...)

inviteUser(options: { roomId: string; userId: string; }) => Promise<void>

| Param | Type | | ------------- | ------------------------------------------------ | | options | { roomId: string; userId: string; } |


kickUser(...)

kickUser(options: { roomId: string; userId: string; reason?: string; }) => Promise<void>

| Param | Type | | ------------- | ----------------------------------------------------------------- | | options | { roomId: string; userId: string; reason?: string; } |


banUser(...)

banUser(options: { roomId: string; userId: string; reason?: string; }) => Promise<void>

| Param | Type | | ------------- | ----------------------------------------------------------------- | | options | { roomId: string; userId: string; reason?: string; } |


unbanUser(...)

unbanUser(options: { roomId: string; userId: string; }) => Promise<void>

| Param | Type | | ------------- | ------------------------------------------------ | | options | { roomId: string; userId: string; } |


sendTyping(...)

sendTyping(options: { roomId: string; isTyping: boolean; timeout?: number; }) => Promise<void>

| Param | Type | | ------------- | --------------------------------------------------------------------- | | options | { roomId: string; isTyping: boolean; timeout?: number; } |


getMediaUrl(...)

getMediaUrl(options: { mxcUrl: string; }) => Promise<{ httpUrl: string; }>

| Param | Type | | ------------- | -------------------------------- | | options | { mxcUrl: string; } |

Returns: Promise<{ httpUrl: string; }>


getThumbnailUrl(...)

getThumbnailUrl(options: ThumbnailUrlOptions) => Promise<{ httpUrl: string; }>

| Param | Type | | ------------- | ------------------------------------------------------------------- | | options | ThumbnailUrlOptions |

Returns: Promise<{ httpUrl: string; }>


uploadContent(...)

uploadContent(options: UploadContentOptions) => Promise<UploadContentResult>

| Param | Type | | ------------- | --------------------------------------------------------------------- | | options | UploadContentOptions |

Returns: Promise<UploadContentResult>


searchUsers(...)

searchUsers(options: { searchTerm: string; limit?: number; }) => Promise<{ results: UserProfile[]; limited: boolean; }>

| Param | Type | | ------------- | ---------------------------------------------------- | | options | { searchTerm: string; limit?: number; } |

Returns: Promise<{ results: UserProfile[]; limited: boolean; }>


setPresence(...)

setPresence(options: { presence: 'online' | 'offline' | 'unavailable'; statusMsg?: string; }) => Promise<void>

| Param | Type | | ------------- | -------------------------------------------------------------------------------------- | | options | { presence: 'online' | 'offline' | 'unavailable'; statusMsg?: string; } |


getPresence(...)

getPresence(options: { userId: string; }) => Promise<PresenceInfo>

| Param | Type | | ------------- | -------------------------------- | | options | { userId: string; } |

Returns: Promise<PresenceInfo>


getDevices()

getDevices() => Promise<{ devices: DeviceInfo[]; }>

Returns: Promise<{ devices: DeviceInfo[]; }>


deleteDevice(...)

deleteDevice(options: { deviceId: string; auth?: Record<string, unknown>; }) => Promise<void>

| Param | Type | | ------------- | ---------------------------------------------------------------------------------------------- | | options | { deviceId: string; auth?: Record<string, unknown>; } |


verifyDevice(...)

verifyDevice(options: { deviceId: string; }) => Promise<void>

| Param | Type | | ------------- | ---------------------------------- | | options | { deviceId: string; } |


setPusher(...)

setPusher(options: PusherOptions) => Promise<void>

| Param | Type | | ------------- | ------------------------------------------------------- | | options | PusherOptions |


initializeCrypto()

initializeCrypto() => Promise<void>

getEncryptionStatus()

getEncryptionStatus() => Promise<EncryptionStatus>

Returns: Promise<EncryptionStatus>


bootstrapCrossSigning()

bootstrapCrossSigning() => Promise<void>

setupKeyBackup()

setupKeyBackup() => Promise<KeyBackupStatus>

Returns: Promise<KeyBackupStatus>


getKeyBackupStatus()

getKeyBackupStatus() => Promise<KeyBackupStatus>

Returns: Promise<KeyBackupStatus>


restoreKeyBackup(...)

restoreKeyBackup(options?: { recoveryKey?: string | undefined; } | undefined) => Promise<{ importedKeys: number; }>

| Param | Type | | ------------- | -------------------------------------- | | options | { recoveryKey?: string; } |

Returns: Promise<{ importedKeys: number; }>


setupRecovery(...)

setupRecovery(options?: { passphrase?: string | undefined; existingPassphrase?: string | undefined; } | undefined) => Promise<RecoveryKeyInfo>

| Param | Type | | ------------- | ------------------------------------------------------------------ | | options | { passphrase?: string; existingPassphrase?: string; } |

Returns: Promise<RecoveryKeyInfo>


clearAllData()

clearAllData() => Promise<void>

Wipe all local Matrix state (crypto DB, session, caches).


isRecoveryEnabled()

isRecoveryEnabled() => Promise<{ enabled: boolean; }>

Returns: Promise<{ enabled: boolean; }>


recoverAndSetup(...)

recoverAndSetup(options: { recoveryKey?: string; passphrase?: string; }) => Promise<void>

| Param | Type | | ------------- | ----------------------------------------------------------- | | options | { recoveryKey?: string; passphrase?: string; } |


resetRecoveryKey(...)

resetRecoveryKey(options?: { passphrase?: string | undefined; } | undefined) => Promise<RecoveryKeyInfo>

| Param | Type | | ------------- | ------------------------------------- | | options | { passphrase?: string; } |

Returns: Promise<RecoveryKeyInfo>


exportRoomKeys(...)

exportRoomKeys(options: { passphrase: string; }) => Promise<{ data: string; }>

| Param | Type | | ------------- | ------------------------------------ | | options | { passphrase: string; } |

Returns: Promise<{ data: string; }>


importRoomKeys(...)

importRoomKeys(options: { data: string; passphrase: string; }) => Promise<{ importedKeys: number; }>

| Param | Type | | ------------- | -------------------------------------------------- | | options | { data: string; passphrase: string; } |

Returns: Promise<{ importedKeys: number; }>


addListener('syncStateChange', ...)

addListener(event: 'syncStateChange', listenerFunc: (data: SyncStateChangeEvent) => void) => Promise<PluginListenerHandle>

| Param | Type | | ------------------ | ---------------------------------------------------------------------------------------- | | event | 'syncStateChange' | | listenerFunc | (data: SyncStateChangeEvent) => void |

Returns: Promise<PluginListenerHandle>


addListener('messageReceived', ...)

addListener(event: 'messageReceived', listenerFunc: (data: MessageReceivedEvent) => void) => Promise<PluginListenerHandle>

| Param | Type | | ------------------ | ---------------------------------------------------------------------------------------- | | event | 'messageReceived' | | listenerFunc | (data: MessageReceivedEvent) => void |

Returns: Promise<PluginListenerHandle>


addListener('roomUpdated', ...)

addListener(event: 'roomUpdated', listenerFunc: (data: RoomUpdatedEvent) => void) => Promise<PluginListenerHandle>

| Param | Type | | ------------------ | -------------------------------------------------------------------------------- | | event | 'roomUpdated' | | listenerFunc | (data: RoomUpdatedEvent) => void |

Returns: Promise<PluginListenerHandle>


addListener('typingChanged', ...)

addListener(event: 'typingChanged', listenerFunc: (data: TypingEvent) => void) => Promise<PluginListenerHandle>

| Param | Type | | ------------------ | ---------------------------------------------------------------------- | | event | 'typingChanged' | | listenerFunc | (data: TypingEvent) => void |

Returns: Promise<PluginListenerHandle>


addListener('receiptReceived', ...)

addListener(event: 'receiptReceived', listenerFunc: (data: ReceiptReceivedEvent) => void) => Promise<PluginListenerHandle>

| Param | Type | | ------------------ | ---------------------------------------------------------------------------------------- | | event | 'receiptReceived' | | listenerFunc | (data: ReceiptReceivedEvent) => void |

Returns: Promise<PluginListenerHandle>


addListener('presenceChanged', ...)

addListener(event: 'presenceChanged', listenerFunc: (data: PresenceChangedEvent) => void) => Promise<PluginListenerHandle>

| Param | Type | | ------------------ | ---------------------------------------------------------------------------------------- | | event | 'presenceChanged' | | listenerFunc | (data: PresenceChangedEvent) => void |

Returns: Promise<PluginListenerHandle>


removeAllListeners()

removeAllListeners() => Promise<void>

Interfaces

SessionInfo

| Prop | Type | | ------------------- | ------------------- | | accessToken | string | | userId | string | | deviceId | string | | homeserverUrl | string |

LoginOptions

| Prop | Type | | ------------------- | ------------------- | | homeserverUrl | string | | userId | string | | password | string |

LoginWithTokenOptions

| Prop | Type | | ------------------- | ------------------- | | homeserverUrl | string | | accessToken | string | | userId | string | | deviceId | string |

RoomSummary

| Prop | Type | | ----------------- | --------------------------------------------------- | | roomId | string | | name | string | | topic | string | | memberCount | number | | isEncrypted | boolean | | unreadCount | number | | lastEventTs | number | | membership | 'join' | 'invite' | 'leave' | 'ban' | | avatarUrl | string | | isDirect | boolean |

RoomMember

| Prop | Type | | ----------------- | --------------------------------------------------- | | userId | string | | displayName | string | | membership | 'join' | 'invite' | 'leave' | 'ban' | | avatarUrl | string |

SendMessageOptions

| Prop | Type | Description | | -------------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | | roomId | string | | | body | string | | | msgtype | 'm.text' | 'm.notice' | 'm.emote' | 'm.image' | 'm.audio' | 'm.video' | 'm.file' | | | fileUri | string | | | fileName | string | | | mimeType | string | | | fileSize | number | | | duration | number | Audio/video duration in milliseconds (sets info.duration per Matrix spec) | | width | number | Image/video width in pixels (sets info.w per Matrix spec) | | height | number | Image/video height in pixels (sets info.h per Matrix spec) |

EditMessageOptions

| Prop | Type | Description | | -------------- | --------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | | roomId | string | | | eventId | string | | | newBody | string | | | msgtype | 'm.text' | 'm.notice' | 'm.emote' | 'm.image' | 'm.audio' | 'm.video' | 'm.file' | Required when editing a media message; must match the original msgtype | | fileUri | string | New file to replace the media content (optional for caption-only edits) | | fileName | string | | | mimeType | string | | | fileSize | number | | | duration | number | Audio/video duration in milliseconds | | width | number | Image/video width in pixels | | height | number | Image/video height in pixels |

SendReplyOptions

| Prop | Type | Description | | -------------------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | | roomId | string | | | body | string | | | replyToEventId | string | | | msgtype | 'm.text' | 'm.notice' | 'm.emote' | 'm.image' | 'm.audio' | 'm.video' | 'm.file' | | | fileUri | string | | | fileName | string | | | mimeType | string | | | fileSize | number | | | duration | number | Audio/video duration in milliseconds (sets info.duration per Matrix spec) | | width | number | Image/video width in pixels (sets info.w per Matrix spec) | | height | number | Image/video height in pixels (sets info.h per Matrix spec) |

MatrixEvent

| Prop | Type | Description | | -------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------- | | eventId | string | | | roomId | string | | | senderId | string | | | type | string | | | content | Record<string, unknown> | | | originServerTs | number | | | status | 'sending' | 'sent' | 'delivered' | 'read' | Delivery/read status for own messages: 'sending' | 'sent' | 'delivered' | 'read' | | readBy | string[] | User IDs that have read this event | | unsigned | Record<string, unknown> | Unsigned data (e.g. m.relations for edits, transaction_id for local echo) |

ThumbnailUrlOptions

| Prop | Type | | ------------ | ------------------------------ | | mxcUrl | string | | width | number | | height | number | | method | 'scale' | 'crop' |

UploadContentResult

| Prop | Type | | ---------------- | ------------------- | | contentUri | string |

UploadContentOptions

| Prop | Type | | -------------- | ------------------- | | fileUri | string | | fileName | string | | mimeType | string |

UserProfile

| Prop | Type | | ----------------- | ------------------- | | userId | string | | displayName | string | | avatarUrl | string |

PresenceInfo

| Prop | Type | | ------------------- | --------------------------------------------------- | | presence | 'online' | 'offline' | 'unavailable' | | statusMsg | string | | lastActiveAgo | number |

DeviceInfo

| Prop | Type | Description | | ---------------------------- | -------------------- | ------------------------------------------------- | | deviceId | string | | | displayName | string | | | lastSeenTs | number | | | lastSeenIp | string | | | isCrossSigningVerified | boolean | Whether this device is verified via cross-signing |

PusherOptions

| Prop | Type | | ----------------------- | ---------------------------------------------- | | pushkey | string | | kind | string | null | | appId | string | | appDisplayName | string | | deviceDisplayName | string | | lang | string | | data | { url: string; format?: string; } |

EncryptionStatus

| Prop | Type | | -------------------------- | ----------------------------------------------------------------- | | isCrossSigningReady | boolean | | crossSigningStatus | CrossSigningStatus | | isKeyBackupEnabled | boolean | | keyBackupVersion | string | | isSecretStorageReady | boolean |

CrossSigningStatus

| Prop | Type | | -------------------- | -------------------- | | hasMaster | boolean | | hasSelfSigning | boolean | | hasUserSigning | boolean | | isReady | boolean |

KeyBackupStatus

| Prop | Type | | ------------- | -------------------- | | exists | boolean | | version | string | | enabled | boolean |

RecoveryKeyInfo

| Prop | Type | | ----------------- | ------------------- | | recoveryKey | string |

PluginListenerHandle

| Prop | Type | | ------------ | ----------------------------------------- | | remove | () => Promise<void> |

SyncStateChangeEvent

| Prop | Type | | ----------- | ----------------------------------------------- | | state | SyncState | | error | string |

MessageReceivedEvent

| Prop | Type | | ----------- | --------------------------------------------------- | | event | MatrixEvent |

RoomUpdatedEvent

| Prop | Type | | ------------- | --------------------------------------------------- | | roomId | string | | summary | RoomSummary |

TypingEvent

| Prop | Type | | ------------- | --------------------- | | roomId | string | | userIds | string[] |

ReceiptReceivedEvent

| Prop | Type | Description | | ------------- | ------------------- | ---------------------------------- | | roomId | string | | | eventId | string | The event that was read | | userId | string | The user who sent the read receipt |

PresenceChangedEvent

| Prop | Type | | -------------- | ----------------------------------------------------- | | userId | string | | presence | PresenceInfo |

Type Aliases

SyncState

'INITIAL' | 'SYNCING' | 'ERROR' | 'STOPPED'

Record

Construct a type with a set of properties K of type T

{ [P in K]: T; }

Development

Setup

npm install

Build

npm run build

Test

npm test              # run once
npm run test:watch    # watch mode

The test suite covers the web layer (~98 tests) using Vitest with jsdom, mocking matrix-js-sdk at the module level.

Verify All Platforms

npm run verify        # builds + tests web, builds Android, builds iOS
npm run verify:web    # web only
npm run verify:android
npm run verify:ios

Lint & Format

npm run lint          # check
npm run fmt           # auto-fix

Example App

An example app is included in example-app/ for manual testing against a local homeserver:

cd example-app
npm install
npm run dev

License

MIT