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

@doync/mobile

v0.4.0

Published

doync React Native client: single-process ClientEngine over op-sqlite

Readme

@doync/mobile

Single-process React Native client for doync: one in-process engine, one WebSocket, one op-sqlite Replica.

Install

pnpm add @doync/mobile @doync/client @doync/core @doync/react
pnpm add @op-engineering/op-sqlite   # bare RN
# or, for Expo dev builds:
npx expo install @op-engineering/op-sqlite

@op-engineering/op-sqlite is a native module. It needs a dev build (npx expo prebuild / EAS Build / bare RN native compile). It does not run in Expo Go. react-native is also a peer (AppState foreground reconnect); the library never pins either peer's version.

There is no Expo SQLite / Expo Go path. Expo apps use a prebuild.

Wiring

One call boots the client. Pass the same shared definition module the web app uses (schema / queries / mutations from createDoync / createDoyncDrizzle), the Mirror WebSocket URL (required on RN — no origin to derive from), and one authData value (or null).

import { createMobileClient } from '@doync/mobile'
import { DoyncProvider, useQuery, useMutation, useConnectionStatus } from '@doync/react'

// Shared with the web app — one data layer for both clients.
import { ctxValidationSchema, schema, queries, mutations } from './shared/data'

const client = createMobileClient({
  name: 'tasks', // Database name; defaults to 'doync'
  schema,
  queries,
  mutations,
  url: 'wss://example.com/sync',
  authData: { userId, token, ctx }, // or null for anonymous
  ctxValidationSchema, // types authData.ctx / updateAuth and validates here
  // name, // optional; default 'doync'; use when you have multiple DO databases
  // logoutBehavior: 'keep', // optional; omit leaves a stored choice alone
})

// Same hooks, same provider — nothing RN-specific in the React layer.
export function App() {
  return (
    <DoyncProvider client={client}>
      <TaskList />
    </DoyncProvider>
  )
}

createMobileClient is sync. Pass the same stable client object into <DoyncProvider> for the life of the app; drive login/logout through client.updateAuth rather than reconstructing.

Options

| Option | Required | Notes | | --- | --- | --- | | schema | yes | Consumer schema. | | queries | yes | Nested query tree (parity with web's createWorker). | | mutations | yes | Nested mutation tree. | | url | yes | Mirror WebSocket URL. | | authData | yes | AuthData \| null{ userId, token?, ctx } or null anonymous. | | name | no | Database name; default 'doync'. | | logoutBehavior | no | Initial 'keep' | 'forget' for this identity. Omit leaves a previously stored choice alone across restarts. |

Auth and logout

updateAuth asserts the whole identity (AuthData | null) atomically (shared semantics with @doync/client / @doync/web). Same userId (including both null) updates auth in place; a changed userId swaps to that identity's Replica while keeping the client object identity stable so <DoyncProvider> and live hooks do not remount.

client.updateAuth({ userId: nextUserId, token: nextToken, ctx: nextCtx })
client.updateAuth(null) // logout

Per-identity logout retention (LogoutBehavior — same policy as @doync/client / @doync/web):

  • 'keep' (default) — outgoing Replica and pending queue stay on disk across a userId change. Logging back in boots warm.
  • 'forget' — outgoing Replica is deleted from the device. Use on a shared device.
const client = createMobileClient({
  /* … */,
  logoutBehavior: 'forget',
})

client.setLogoutBehavior('keep') // flip later; honored at the next swap

Recovery verbs (same resync / forget contract as @doync/client):

client.resync() // wipe Replica / Memberships / cookie; drop pendings; mint fresh clientId
client.forget() // erase the active identity's local data
client.forget('other-user-id') // delete another identity's file only
client.close() // tear down socket + handle; leaves the durable file intact

When you believe the network is back, connect your own NetInfo logic or a manual "Reconnect" button to client.reconnect(). The client already reconnects on its own when the app returns to the foreground; reconnect() is for the hints only you can see. It is safe to call anytime.

Connection and schema status for banners (same shapes as web / useSchemaStatus):

client.connectionStatus // 'connecting' | 'connected' | 'disconnected' | 'error' | 'needs-auth'
client.onConnectionChange(() => {
  /* re-read client.connectionStatus */
})

client.schemaStatus // null when nominal, else { kind, message }
client.onSchemaChange(() => {
  /* re-read client.schemaStatus */
})

Usage with @doync/react

Hooks are environment-agnostic. After the Provider is mounted, web and mobile components share one shape:

import {
  useQuery,
  useMutation,
  useConnectionStatus,
  useSchemaStatus,
} from '@doync/react'
import { queries, mutations } from './shared/data'

function TaskList({ projectId }: { projectId: string }) {
  const [tasks, status] = useQuery(queries.tasks.open({ projectId }))
  const createTask = useMutation(mutations.task.create)
  const connection = useConnectionStatus()
  const schema = useSchemaStatus()

  return (
    <>
      <Text>{connection}</Text>
      {schema ? <Text>{schema.message}</Text> : null}
      {status.status !== 'complete' && tasks.length === 0 ? (
        <Text>Loading…</Text>
      ) : (
        tasks.map((t) => <Text key={t.id}>{t.title}</Text>)
      )}
      <Button
        onPress={() => {
          void createTask({ id: newId(), projectId, title: 'New' }).client
        }}
      />
    </>
  )
}

See @doync/react for the full hook surface (useQueryOnce, useLocalQuery, falsy "no query", options).

One createMobileClient call = one Database name. An app with several synced databases runs several clients with distinct names and provides each where needed.

Public surface

| Export | Role | | --- | --- | | createMobileClient / CreateMobileClientOptions | Boot the RN client | | MobileClient | Returned client (subscribe / warmup / once / local / mutate + auth / reconnect / recovery / close) | | DoyncClient / View / OnceView / ViewStatus / QueryStatus / ConnectionStatus / SchemaEvent / SchemaEventKind / FalsyQuery / MutationOptions / MutationResult / LogoutBehavior / SubscribeOptions / PreloadOptions / PreloadHandle / WarmupOptions / WarmupHandle | Client call-surface family (re-exported from @doync/client) |

Internal API

The main entry (.) is the semver-governed public surface documented here. Anything imported from @doync/mobile/internal may change in any release, including patches, without notice — use it only if you accept that risk.