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

openexit

v0.1.0

Published

OpenExit Protocol toolkit for portable application state

Readme

OpenExit

OpenExit is a developer toolkit for moving application state between software systems. A vendor's source adapter maps proprietary data into canonical records. OpenExit writes a portable bundle; a destination adapter maps those records into another application's state.

This package implements OpenExit Protocol version 1.0. The public protocol name can evolve independently of the stable openexit.bundle wire-format identifier.

Install and run

Requires Node.js 20 or newer. There are no runtime dependencies.

npm install -g openexit
openexit --version
openexit --help

From this repository, the included adapter demonstrates export and import:

openexit export --adapter ./examples/adapter.js --dir ./bundle --chunk-size 2
openexit inspect --dir ./bundle
openexit import --adapter ./examples/adapter.js --dir ./bundle --batch-size 1

During development, node src/cli.js works in place of openexit. The example reads examples/customers.ndjson and writes account-shaped records to examples/imported/. Set OPENEXIT_INPUT and OPENEXIT_OUTPUT to use other paths. After an interrupted export, repeat the export command with --resume. Import resumes automatically.

Adapter contract

An adapter module exports source, target, or both. A source provides:

export const source = {
  describe() {
    return { name: 'my-app', schemaVersion: '1', snapshot: 'stable-snapshot-id' };
  },
  async readPage({ cursor, limit }) {
    // Return at most limit canonical records.
    return { records: [], nextCursor: cursor, done: true };
  }
};

describe() identifies the source schema and snapshot. A cursor identifies the first unread record. The source must keep record order and contents stable during export and resume. OpenExit commits the next cursor only after writing the corresponding chunk and manifest.

A target provides:

export const target = {
  describe() { return { name: 'my-destination', instance: 'tenant-123' }; },
  async writeBatch(records, { bundleId, chunkIndex, batchIndex }) {
    // Apply records to the destination.
  }
};

describe() identifies the exact destination for its checkpoint. writeBatch must be idempotent. A stopped import can replay its last chunk. Upsert by (type, id), or deduplicate with the supplied bundle, chunk, and batch identity. The included example uses deterministic output filenames.

Canonical records

Each record requires type, id, and state:

{"type":"customer","id":"c-1","state":{"name":"Ada"},"links":{"team":["t-1"]},"meta":{"sourceVersion":"1"}}

state, optional links, and optional meta contain JSON values. Adapters own mappings between their proprietary schemas and this interface. The JavaScript API exports exportBundle, importBundle, inspectBundle, and validators. Export and import accept an async transform(record, context) callback; returning null drops a record.

Bundle format

A bundle directory contains manifest.json and numbered NDJSON chunk files. The manifest records the OpenExit Protocol version, source snapshot, chunk order, record counts, SHA-256 digests, resume cursor, and completion state. Import checks each chunk's digest before applying it, validates records as it reads them, and checkpoints after each chunk. See PROTOCOL.md for the wire format.

Current limitations

  • Export holds one page in memory and import holds one batch. Very large individual records or chunks can still use substantial memory.
  • Bundles use local files and NDJSON. Remote storage, compression, encryption, and other bundle encodings are not implemented.
  • Source adapters must provide stable snapshots. Target adapters must make writes idempotent. There is no distributed transaction across applications.
  • One process should write a bundle or target checkpoint at a time; multi-process locking is not implemented.
  • The toolkit is JavaScript today. The documented bundle format is intended for other languages, but other language SDKs are not included.
  • OpenExit Protocol version 1.0 validates the canonical envelope and major wire version; it does not provide a domain-schema migration registry.

Licensed under MIT. The example adapter and sample input are included in the npm package.