Skip to content
himanshuSri24Public

About

Tools for the Firebase Firestore console: search every list, filter a document's fields, and copy whole documents as JSON. Read-only.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Firepanel

CI

Tools for the Firebase Firestore console: a search box on every list, a filter for the fields of an open document, and a Copy JSON button that copies a whole document without you expanding it first.

Firepanel is an independent project. It is not affiliated with, endorsed by, or sponsored by Google. Firebase and Firestore are trademarks of Google LLC.

It is read-only by construction. It holds one permission (clipboardWrite), runs only on console.firebase.google.com, talks to no network of its own, and never clicks a control that could add, edit or delete data — see Safety.

What it does

Search any list

Every list panel gets a filter box at the top — collections, documents inside a collection, and the subcollections of a document. Type to filter that list, click a result to open it, Esc to clear.

The console renders these lists through a virtual scroller, so only the rows on screen exist in the DOM. Names are collected as rows render, and the first time you type the extension scrolls the list once (time-boxed to 3s) to pick up the rest. If that ran out of time, the results end with scan further — a 15s sweep you trigger yourself, so a huge collection is never paged through automatically.

Documents: filter what is loaded, or open an ID outright

A collection can hold more documents than the console will ever page into the list, and Firestore cannot search document IDs by substring — no client-side trick changes that. So the documents box does two things: it filters the IDs loaded so far, and it offers Open "" (or just press Enter) to navigate straight to that exact ID, whether or not the list ever reached it.

The direct-open URL is derived from the panel's position in the panel chain, which mirrors the data path — panel 1 lists the documents of path segment 0, and so on. It is offered for documents only; a collections panel's place in that chain is ambiguous.

Prefix search over the whole collection

Typing a partial ID offers Search IDs starting with "…" (or press Enter). That runs server-side over the entire collection — not over what the list happened to load — and returns instantly regardless of collection size.

It works by building the console's own query-builder URL. The query builder accepts a Document ID condition, which it encodes in the URL as the reserved __name__ field, so the extension writes that URL and opens it in a new tab — the panel you were browsing stays where it was:

?view=query-view
&query=2|LIM|2/50|WH|1|8/__name__|GTE|STR|3/p60
&scopeType=collection&scopeName=orders

<clauses>|LIM|<len>/<limit>|WH|<conditions>|<len>/<field>|<op>|<type>|<len>/<value>, every value prefixed with its length. src/query-view.ts builds it; that file is the only thing to change if the console's encoding moves.

The condition is >= with a limit of 50, so results start at your prefix and run on past it in ID order — the matches are the ones at the top. The prefix is case-sensitive, because it is a range over byte order.

The plain filter box still filters the IDs already loaded, case-insensitively, which is faster when what you want is on screen — matches show the matched span highlighted. Shift+Enter, or the Open row, opens the typed ID as an exact document, also in a new tab.

Why the loaded list is not enough

The data view stops serving rows into a document list after a few hundred (~600 in the collections we tried), no matter how far you scroll — a console limit, not a Firestore one. Neither can the panel's funnel filter reach document IDs: it validates against real field names and rejects __name__. Hence the query-builder URL, which is subject to neither.

Filter a document's fields by key or value

An open document gets a Filter fields by key or value box above its field list. Type part of a key or of a leaf value; everything that neither matches nor contains a match is hidden. A match keeps its whole value on screen, so matching a map shows what is inside it. Esc clears.

The count on the right reports the two kinds separately — 3 keys · 2 values — because a value hit shows a row whose key does not contain what you typed, and an unlabelled total makes that look like a bug.

Only leaf fields hold a value: a map's value is its children, so a map is never matched on its contents directly, though a match inside one still surfaces it as the parent.

Nested keys and values only exist in the DOM once their branch is open, so the first search expands the document — the same read-only expansion the copy button uses. That means the first keystroke on a large document takes a moment, and the document stays expanded afterwards.

The filter is display-only: Copy JSON always copies the full document, not the filtered view.

Copy a whole document as JSON

Open a document and click Copy JSON in the breadcrumb bar. Every collapsed map and array is expanded first, so the copied JSON is the complete document — not just the branches you happened to have open.

{
  "category": "UTILITY",
  "config": { "retries": 3, "targets": ["sms", "whatsapp"] },
  "createdAt": "August 27, 2026 at 7:15:00 AM UTC+5:30"
}

Timestamps, geopoints and references come out as the string the console displays, because that is all the rendered DOM holds. String fields containing stringified JSON are unwrapped for readability — flip PARSE_STRINGIFIED_JSON in src/document-parser.ts if you want the raw string.

Safety

The reason this is a frontend-only extension is that it must not be able to change anything. Two rules hold that up:

  • No credentials, no API calls, no background page. It reads the DOM the console has already rendered.
  • Expanding a document only ever clicks a disclosure control: an element with aria-expanded="false", or, when the console ships no ARIA, an element sitting to the left of the field key (where the console puts the chevron and never puts a row action). Anything labelled edit / delete / add / copy is excluded, and a field whose disclosure cannot be identified is skipped and reported — the button then reads Copied (N not expanded).

Install

From the Chrome Web Store (recommended — it updates itself):

Listing link goes here once the first review has passed.

Or from a release: download firepanel.zip from Releases, extract it to a folder you will keep, then chrome://extensions/ → Developer mode on → Load unpacked → select that folder. Chrome reads the folder from disk on every start, so it has to stay put.

Development

npm i
npm test          # unit tests
npm run typecheck
npm run package   # → firepanel.zip, and dist/ to load unpacked

dist/manifest.json is generated from package.json at build time, so the version, name and description have one source of truth and the store's field limits are checked before submission rather than after.

To release: bump the version in package.json, tag it vX.Y.Z and push the tag. CI checks that the tag matches, runs the tests, builds the zip and creates the GitHub release. Nothing is built by hand.

Layout

File Role
src/console-dom.ts Every console selector, in one place
src/list-filter.ts PanelListFilter per list, PanelFilterManager reconciles them
src/field-filter.ts Key filter for an open document's fields
src/document-expander.ts Opens collapsed maps/arrays before a copy or field search
src/document-parser.ts Fields DOM → plain object
src/copy-button.ts The breadcrumb button and clipboard write
src/console-watcher.ts Re-injects across SPA navigation, ignores our own mutations
src/theme.ts Resolves colours from what the console actually paints, light or dark

Theme

The console has light and dark themes and marks neither on the elements we inject into. Rather than assume, src/theme.ts reads the first painted ancestor behind an injected element and uses that as its background, decides light-vs-dark from the page's own luminance, and picks the matching accent (#1a73e8 light, #8ab4f8 dark). Hovers, borders and dividers are neutral greys so they read on either. Flipping the theme with the page open remounts everything, so nothing keeps stale colours.

When the console changes under us

Firebase's markup is not a public contract, so a console release can break the selectors. Everything the extension matches on lives in src/console-dom.ts, and the expander is the piece most likely to drift. Open a document with a collapsed map, run window.__fctDebug() in DevTools, and it prints the real markup of one collapsed field's click target — enough to pin the selector.

Privacy

Firepanel collects nothing, stores nothing and sends nothing anywhere. The full policy is in PRIVACY.md.

Licence

MIT.

Prior art: the collection-filter approach (filtering a virtual-scrolled list via a harvested name cache and an overlay) is taken from xorrier/copy-fb-doc by Amrit Sharma.

About

Tools for the Firebase Firestore console: search every list, filter a document's fields, and copy whole documents as JSON. Read-only.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages