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

firestore-indexes-diff

v2.0.0

Published

Displays differences between two Firestore index config files

Readme

🔍 Firestore Indexes Diff

npm license typescript vitest

A CLI tool that reports which Firestore indexes and field overrides exist in one index configuration file but are missing from another, so you can catch gaps before deploying across environments.

✨ Features

  • 🚀 Compare Firestore index configurations between environments
  • 📊 Report indexes and field overrides that the target file lacks
  • 🧠 Normalises the implicit trailing __name__ field, so a server export compares correctly against a hand-written file
  • 💡 Colourful CLI output when attached to a terminal, quiet and parseable when piped
  • 📁 Export differences to JSON files for further analysis

📋 Requirements

  • Node.js 22.12 or newer. Earlier versions cannot parse the JSON import attribute this CLI uses. npm install will warn you if your Node is too old.

You also need two Firestore index configuration files. See the Cloud Firestore Index Definition Reference for the file format.

🚀 Quick Start

Using pnpm dlx (Recommended)

pnpm dlx firestore-indexes-diff --source dev.json --target prod.json
# npx firestore-indexes-diff --source dev.json --target prod.json
# yarn dlx firestore-indexes-diff --source dev.json --target prod.json

Global Installation

pnpm add -g firestore-indexes-diff
# npm install -g firestore-indexes-diff
# yarn global add firestore-indexes-diff

diff-indexes --source dev.json --target prod.json

Try it against the bundled samples

The repository ships two sample files. sample/dev_indexes.json declares a users index and a posts index; sample/prod_indexes.json declares only the users one.

git clone https://github.com/omar-dulaimi/firestore-indexes-diff.git
cd firestore-indexes-diff
pnpm install
pnpm run build

node lib/bin/index.js --source sample/dev_indexes.json --target sample/prod_indexes.json
cat diff-indexes.json

That writes a diff-indexes.json holding the one posts index that production is missing. No diff-field-overrides.json appears, because neither sample file declares any field overrides.

⚙️ Options

| Option | Alias | Type | Required | Description | | ----------- | ----- | ------ | -------- | ------------------------ | | --source | -s | string | ✅ | Source indexes file path | | --target | -t | string | ✅ | Target indexes file path | | --version | -v | | | Show version number | | --help | -h | | | Show help |

Paths may be relative to the current directory or absolute.

📤 Output

The comparison is one-directional: it answers "what is in the source file but not in the target file". Results are written to the current directory.

| File | Contents | | --------------------------- | ---------------------------------------------------- | | diff-indexes.json | Indexes present in the source, missing in target | | diff-field-overrides.json | Field overrides present in source, missing in target |

Each file is written only when there is something to report, and an existing file is deleted when there is nothing to report. That keeps the presence of a file a truthful signal, so a CI check can test for it without being tripped up by output left over from an earlier run.

The exit code is 0 for a successful comparison, whether or not differences were found, and 1 if a file could not be read, is not valid JSON, or is not a Firestore index file.

🔬 How indexes are compared

Two indexes are considered the same when their collectionGroup, queryScope, apiScope, density, multikey, unique and fields all agree, following the equality rules that firebase-tools uses.

Some details worth knowing:

  • The implicit __name__ field is normalised. Firestore appends a trailing __name__ field to every composite index, so a file produced by firebase firestore:indexes always carries it while a hand-written firestore.indexes.json never does. Both sides are normalised before comparing, otherwise every index in a server export would be reported as missing.
  • Field order within an index matters, because it is significant to Firestore. [a, b] and [b, a] are different indexes.
  • Key order inside the JSON does not matter. A field written as {"fieldPath": "a", "order": "ASCENDING"} matches one written as {"order": "ASCENDING", "fieldPath": "a"}.
  • An omitted optional value equals its default. An absent apiScope matches an explicit ANY_API, and an absent ttl on a field override matches an explicit ttl: false, which is what a server export writes.
  • Field override index lists are order-insensitive, matching firebase-tools: what matters is the set of enabled modes, not the order they appear in.

🔄 Workflow Examples

Development to Production Comparison

# Which indexes does production not have yet?
pnpm dlx firestore-indexes-diff --source dev-indexes.json --target prod-indexes.json

Staging Environment Sync

# Ensure staging has all the indexes from development
pnpm dlx firestore-indexes-diff --source dev-indexes.json --target staging-indexes.json

CI/CD Integration

firebase firestore:indexes needs the Firebase CLI and credentials for the project, so this snippet assumes both are already configured.

- name: Check Firestore Index Completeness
  run: |
    # Export the indexes currently deployed to production.
    firebase firestore:indexes --project "$PROJECT_ID" > prod-indexes.json

    # Compare the committed index file against what is deployed.
    npx firestore-indexes-diff --source firestore.indexes.json --target prod-indexes.json

    # Fail if anything in the committed file is not deployed yet.
    if [ -f "diff-indexes.json" ]; then
      echo "Missing indexes detected!"
      cat diff-indexes.json
      exit 1
    fi

Multi-Environment Validation

# Check multiple environments in sequence
for env in dev staging prod; do
  echo "Checking $env environment..."
  npx firestore-indexes-diff \
    --source master-indexes.json \
    --target "$env-indexes.json"
done

🛠️ Development

# Clone the repository
git clone https://github.com/omar-dulaimi/firestore-indexes-diff.git
cd firestore-indexes-diff

# Install dependencies
pnpm install

# Compile TypeScript to lib/
pnpm run build

# Compile in watch mode
pnpm run dev

# Lint and run the test suite
pnpm run lint
pnpm run test:run

Verifying the published artifact

The test suite includes end-to-end tests that spawn the compiled binary, because unit tests that import the source modules directly cannot see a failure in the CLI entry point. On top of that, this script packs the tarball, installs it into an empty directory and runs the documented command against it:

bash ./scripts/verify-package.sh

CI runs it on Node 22 and 24 for every push.

📝 License

MIT © Omar Dulaimi