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

@kalisio/feathers-automerge-server

v0.3.8

Published

Feathers service for Automerge server

Readme

@kalisio/feathers-automerge-server

Package to set up an automerge sync server that synchronizes documents with a Feathers API.

Usage

As a sync server

In your Feathers application, add the following to your app file:

import { automergeServer } from '@kalisio/feathers-automerge-server'

//...
app.configure(services)
app.configure(
  automergeServer({
    directory: '../../data/automerge',
    serverId: 'test-server',
    syncServicePath: 'automerge',
    async authenticate(accessToken) {
      if (!accessToken) {
        return false
      }
      // Check with the local authentication service if the token is valid
      await app.service('authentication').create({
        strategy: 'jwt',
        accessToken
      })

      return true
    },
    async initializeDocument(servicePath, query) {
      if (servicePath === 'todos') {
        const { username } = query
        return app.service('todos').find({
          paginate: false,
          query: { username }
        })
      }

      return null
    },
    async getDocumentsForData(servicePath, data, documents) {
      if (servicePath === 'todos') {
        return documents.filter(doc => data.username === doc.query.username)
      }

      return []
    },
    async canAccess(query, user) {
      // Only allow access to documents where the query username matches the authenticated user
      return query.username === user?.username
    }
  })
)

Server to server synchronization

import { automergeServer } from '@kalisio/feathers-automerge-server'

//...
app.configure(services)
app.configure(
  automergeServer({
    syncServerUrl: 'https://server.com',
    directory: '../../data/automerge',
    serverId: 'test-server',
    syncServicePath: 'automerge',
    async getAccessToken() {
      const response = await fetch('http://localhost:3030/authentication', {
        body: JSON.stringify({
          strategy: 'local',
          email: '[email protected]',
          password: 'test'
        }),
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        }
      })
      const { accessToken } = await response.json()
      return accessToken
    },
    async initializeDocument(servicePath, query) {
      if (servicePath === 'todos') {
        const { username } = query
        return app.service('todos').find({
          paginate: false,
          query: { username }
        })
      }

      return null
    },
    async getDocumentsForData(servicePath, data, documents) {
      if (servicePath === 'todos') {
        return documents.filter(doc => data.username === doc.query.username)
      }

      return []
    },
    async canAccess(query, user) {
      // Only allow access to documents where the query username matches the authenticated user
      return query.username === user?.username
    }
  })
)

Options

The following options are available:

Common Options

  • directory: string: The directory where the automerge repository data will be stored. The root document ID will be automatically stored in a automerge-server.json file in this directory.
  • serverId: string: A unique identifier for this server instance (used to track data source).
  • syncServicePath: The service path where the automerge sync service will be mounted (e.g., 'automerge').
  • canAccess(query: Query, params: Params): Promise<boolean>: An async function that controls access to documents based on the query and service call params. Called for all operations when a provider is present in params (external calls).
  • initializeDocument: (servicePath: string, query: Query, documents: SyncServiceInfo[]) => Promise<unknown[]>: An async function that initializes document data for a given service path and query. Called when creating new documents.
  • getDocumentsForData: (servicePath: string, data: any, documents: SyncServiceInfo[]) => Promise<SyncServiceInfo[]>: An async function that determines which documents should be updated when service data changes.
  • getInitialDocuments: (app: Application) => Promise<SyncServiceInfo>: Return the documents to load when there are no local documents on this server. This is useful when running as a client (syncServerUrl is pointing to another server) to determine which documents should be synced with this instance.

Server Options

  • syncServerWsPath?: string: The websocket path for the local sync server
  • authenticate: (accessToken: string | null) => Promise<boolean>: Authenticate an access token that was passed to the connection of the local sync server.

Client Options

  • syncServerUrl?: string: Connect to another remote sync server instead (for server to server synchronization)
  • getAccessToken?: () => Promise<string>: Get an access token for the remote sync server.