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

@molecule/app-contacts

v1.0.1

Published

Contacts access interface for molecule.dev

Readme

@molecule/app-contacts

Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit src/index.ts JSDoc, not this file.

Contacts / address-book access interface for molecule.dev.

Framework-agnostic core for reading, searching, creating, updating, deleting, and picking device contacts through a swappable ContactsProvider, plus pure helpers (formatDisplayName, getPrimaryPhone, getPrimaryEmail, formatPhoneNumber, getInitials) that work on Contact objects from any provider.

Quick Start

import {
  getAll,
  getPermissionStatus,
  hasProvider,
  pick,
  requestPermission,
  formatDisplayName,
} from '@molecule/app-contacts'

async function importContacts(): Promise<string[]> {
  if (!hasProvider()) return [] // no provider wired — feature-gate the UI
  if ((await getPermissionStatus()) !== 'granted') {
    const status = await requestPermission() // from a user gesture
    if (status !== 'granted') return []
  }
  const contacts = await getAll({ sortBy: 'name' })
  return contacts.map((c) => formatDisplayName(c))
}

async function pickOne(): Promise<void> {
  const [selected] = await pick({ multiple: false })
  if (selected) console.log(formatDisplayName(selected))
}

Type

native

Installation

npm install @molecule/app-contacts @molecule/app-bond @molecule/app-i18n

API

Interfaces

Contact

Full contact record with name, phones, emails, addresses, organization, and metadata.

interface Contact {
  /** Contact ID */
  id: string
  /** Contact name */
  name: ContactName
  /** Phone numbers */
  phones?: PhoneNumber[]
  /** Email addresses */
  emails?: EmailAddress[]
  /** Postal addresses */
  addresses?: PostalAddress[]
  /** Organization info */
  organization?: Organization
  /** Birthday (ISO date string) */
  birthday?: string
  /** Note/memo */
  note?: string
  /** Photo as base64 data URL */
  photo?: string
  /** URLs (websites, social) */
  urls?: { url: string; label?: string }[]
  /** Custom fields */
  customFields?: Record<string, string>
}

ContactName

Structured name components for a contact (given, family, middle, prefix, suffix, display).

interface ContactName {
  /** Full display name */
  display?: string
  /** Given/first name */
  given?: string
  /** Middle name */
  middle?: string
  /** Family/last name */
  family?: string
  /** Name prefix (e.g., 'Mr.', 'Dr.') */
  prefix?: string
  /** Name suffix (e.g., 'Jr.', 'III') */
  suffix?: string
}

ContactPickerOptions

Contact picker options

interface ContactPickerOptions {
  /** Allow multiple selection */
  multiple?: boolean
  /** Fields to request */
  fields?: (keyof Contact)[]
}

ContactQueryOptions

Contact query options

interface ContactQueryOptions {
  /** Search query string */
  query?: string
  /** Fields to include in results */
  fields?: (keyof Contact)[]
  /** Sort by field */
  sortBy?: 'name' | 'created' | 'modified'
  /** Sort direction */
  sortOrder?: 'asc' | 'desc'
  /** Maximum results */
  limit?: number
  /** Results offset */
  offset?: number
}

ContactsCapabilities

Contacts capabilities

interface ContactsCapabilities {
  /** Whether contacts access is supported */
  supported: boolean
  /** Whether reading is supported */
  canRead: boolean
  /** Whether writing is supported */
  canWrite: boolean
  /** Whether contact picker is supported */
  hasPicker: boolean
  /** Whether photos are supported */
  supportsPhotos: boolean
  /** Maximum contacts that can be fetched */
  maxResults?: number
}

ContactsProvider

Contacts provider interface

interface ContactsProvider {
  /**
   * Get all contacts, optionally filtered and sorted.
   * @param options - Query options (search, fields, sorting, pagination).
   * @returns An array of Contact objects matching the query.
   */
  getAll(options?: ContactQueryOptions): Promise<Contact[]>

  /**
   * Get a single contact by its ID.
   * @param id - The contact ID to look up.
   * @returns The matching Contact, or null if not found.
   */
  getById(id: string): Promise<Contact | null>

  /**
   * Search contacts
   * @param query - Search query
   * @param options - Query options
   */
  search(query: string, options?: Omit<ContactQueryOptions, 'query'>): Promise<Contact[]>

  /**
   * Create a new contact
   * @param contact - Contact data
   */
  create(contact: ContactInput): Promise<Contact>

  /**
   * Update an existing contact
   * @param id - Contact ID
   * @param contact - Updated contact data
   */
  update(id: string, contact: Partial<ContactInput>): Promise<Contact>

  /**
   * Delete a contact
   * @param id - Contact ID
   */
  delete(id: string): Promise<void>

  /**
   * Open native contact picker
   * @param options - Picker options
   */
  pick(options?: ContactPickerOptions): Promise<Contact[]>

  /**
   * Get permission status
   */
  getPermissionStatus(): Promise<ContactsPermissionStatus>

  /**
   * Request permission
   */
  requestPermission(): Promise<ContactsPermissionStatus>

  /**
   * Open system settings for contacts permission
   */
  openSettings(): Promise<void>

  /**
   * Get the platform's contacts capabilities.
   * @returns The capabilities indicating which contacts features are supported.
   */
  getCapabilities(): Promise<ContactsCapabilities>
}

EmailAddress

An email address entry for a contact with label (home, work) and primary flag.

interface EmailAddress {
  /** Email address string */
  address: string
  /** Label (e.g., 'home', 'work') */
  label?: string
  /** Whether this is the primary email */
  isPrimary?: boolean
}

Organization

Contact organization

interface Organization {
  /** Company/organization name */
  company?: string
  /** Job title */
  title?: string
  /** Department */
  department?: string
}

PhoneNumber

A phone number entry for a contact with label (mobile, home, work) and primary flag.

interface PhoneNumber {
  /** Phone number string */
  number: string
  /** Label (e.g., 'mobile', 'home', 'work') */
  label?: string
  /** Whether this is the primary phone */
  isPrimary?: boolean
}

PostalAddress

A postal/mailing address for a contact (street, city, state, postal code, country).

interface PostalAddress {
  /** Street address line 1 */
  street?: string
  /** Street address line 2 */
  street2?: string
  /** City */
  city?: string
  /** State/province */
  state?: string
  /** Postal/ZIP code */
  postalCode?: string
  /** Country */
  country?: string
  /** Label (e.g., 'home', 'work') */
  label?: string
  /** Formatted address string */
  formatted?: string
}

Types

ContactInput

Contact creation/update data

type ContactInput = Omit<Contact, 'id'> & { id?: string }

ContactsPermissionStatus

Permission status

type ContactsPermissionStatus = 'granted' | 'denied' | 'limited' | 'prompt' | 'unsupported'

Functions

create(contact)

Create a new contact on the device.

function create(contact: ContactInput): Promise<Contact>
  • contact — The contact data to create.

Returns: The created Contact with its assigned ID.

deleteContact(id)

Delete a contact from the device.

function deleteContact(id: string): Promise<void>
  • id — The ID of the contact to delete.

Returns: A promise that resolves when the contact is deleted.

formatDisplayName(contact, t)

Format a contact's display name from their name parts. Falls back to "Unknown" if no name parts exist.

function formatDisplayName(
  contact: Contact,
  t?: (
    key: string,
    values?: Record<string, unknown>,
    options?: { defaultValue?: string },
  ) => string,
): string
  • contact — The contact to format.
  • t — Optional i18n translation function for the "Unknown" fallback.

Returns: The formatted display name string.

formatPhoneNumber(phone)

Format a phone number for display using basic US formatting. 10-digit numbers become "(xxx) xxx-xxxx", 11-digit numbers with leading 1 become "+1 (xxx) xxx-xxxx".

function formatPhoneNumber(phone: PhoneNumber): string
  • phone — The PhoneNumber to format.

Returns: The formatted phone number string.

getAll(options)

Get all contacts, optionally filtered and sorted.

function getAll(options?: ContactQueryOptions): Promise<Contact[]>
  • options — Query options (search, fields, sorting, pagination).

Returns: An array of Contact objects matching the query.

getById(id)

Get a single contact by its ID.

function getById(id: string): Promise<Contact | null>
  • id — The contact ID to look up.

Returns: The matching Contact, or null if not found.

getCapabilities()

Get the platform's contacts capabilities.

function getCapabilities(): Promise<ContactsCapabilities>

Returns: The capabilities indicating which contacts features are supported.

getInitials(contact)

Get initials from a contact's name (e.g., "JD" for "John Doe"). Falls back to "??" if no name is available.

function getInitials(contact: Contact): string
  • contact — The contact to extract initials from.

Returns: A 1-2 character uppercase string of initials.

getPermissionStatus()

Get the current contacts permission status.

function getPermissionStatus(): Promise<ContactsPermissionStatus>

Returns: The permission status: 'granted', 'denied', 'limited', 'prompt', or 'unsupported'.

getPrimaryEmail(contact)

Get the primary email address for a contact, falling back to the first email.

function getPrimaryEmail(contact: Contact): EmailAddress | undefined
  • contact — The contact to extract the email from.

Returns: The primary EmailAddress, or undefined if the contact has no emails.

getPrimaryPhone(contact)

Get the primary phone number for a contact, falling back to the first number.

function getPrimaryPhone(contact: Contact): PhoneNumber | undefined
  • contact — The contact to extract the phone number from.

Returns: The primary PhoneNumber, or undefined if the contact has no phone numbers.

getProvider()

Get the current contacts provider.

function getProvider(): ContactsProvider

Returns: The active ContactsProvider instance.

hasProvider()

Check if a contacts provider has been registered.

function hasProvider(): boolean

Returns: Whether a ContactsProvider has been bonded.

openSettings()

Open the system settings screen for contacts permissions.

function openSettings(): Promise<void>

Returns: A promise that resolves when the settings screen is opened.

pick(options)

Open the native contact picker dialog.

function pick(options?: ContactPickerOptions): Promise<Contact[]>
  • options — Picker options (multiple selection, requested fields).

Returns: An array of selected Contact objects.

requestPermission()

Request contacts permissions from the user.

function requestPermission(): Promise<ContactsPermissionStatus>

Returns: The resulting permission status after the request.

search(query, options)

Search contacts by name, email, phone, or other fields.

function search(query: string, options?: Omit<ContactQueryOptions, 'query'>): Promise<Contact[]>
  • query — The search query string.
  • options — Additional query options (fields, sorting, pagination).

Returns: An array of matching Contact objects.

setProvider(provider)

Set the contacts provider.

function setProvider(provider: ContactsProvider): void
  • provider — ContactsProvider implementation to register.

update(id, contact)

Update an existing contact on the device.

function update(id: string, contact: Partial<ContactInput>): Promise<Contact>
  • id — The ID of the contact to update.
  • contact — The partial contact data to merge with the existing contact.

Returns: The updated Contact.

Injection Notes

Requirements

Peer dependencies:

  • @molecule/app-bond ^1.0.1
  • @molecule/app-i18n ^1.0.1

Runtime Dependencies

  • @molecule/app-bond

  • @molecule/app-i18n

  • Every accessor THROWS until setProvider() is called — there is no web fallback and no prebuilt provider package ships with molecule (contacts access needs a native container). Gate all contact UI behind hasProvider() and supply your own ContactsProvider from your native runtime.

  • Browsers have no general address-book API. Do not "fall back to web": the closest thing (Contact Picker API) is Chromium-on-Android only, read-only, picker-only — getAll/create/update/delete cannot be implemented on web at all.

  • Request permission from a user gesture at the point of use and handle 'denied'/'limited' — a denied OS prompt is remembered; recovery is openSettings(), not another requestPermission() call.

  • Check getCapabilities() before offering write features: iOS supports 'limited' access where only a subset of contacts is visible.

Translations

Translation strings are provided by @molecule/app-locales-contacts.