drupe
Tiny helper that lets you run Mango selectors anywhere you need a JavaScript predicate.
drupe is inspired by the ergonomics of sift but speaks Mango—the JSON-based query language used by CouchDB and PouchDB. Pass a Mango selector in and get back a function that tests plain JavaScript objects, arrays, or any iterable source.
Why drupe?
- Reuse the same Mango selectors you ship to CouchDB/PouchDB on the client or in tests.
- Drop into
Array.prototype.filter, find, or any higher-order utility—no extra plumbing required.
- Ships as a single function that delegates to PouchDB’s battle-tested
matchesSelector implementation.
- Zero configuration, fully typed, and framework/runtime agnostic.
Installation
Install from JSR:
yarn add @edge.app/drupe
npx jsr add @edgeapp/drupe
Quick start
import { drupe } from "drupe";
const isPublishedTechArticle = drupe({
type: "article",
publishedAt: { $exists: true },
tags: { $in: ["databases", "javascript"] },
});
const articles = [
{
type: "article",
slug: "mango-101",
tags: ["databases"],
publishedAt: "2024-05-02",
},
{ type: "article", slug: "draft-post", tags: ["javascript"] },
{ type: "note", slug: "retro" },
];
const published = articles.filter(isPublishedTechArticle);
The same selector you use in a db.find() call now works as an in-memory predicate.
More examples
All Mango selector operators are supported because drupe simply forwards to PouchDB’s selector engine. For a full reference, see the PouchDB guide on Mango queries and the CouchDB documentation linked above.
import { drupe } from "drupe";
const isHighValueCustomer = drupe({
$and: [{ spend: { $gte: 1000 } }, { status: { $in: ["gold", "platinum"] } }],
});
const customers = [
{ name: "Ada", spend: 3200, status: "platinum" },
{ name: "Lin", spend: 840, status: "gold" },
{ name: "Edsger", spend: 1500, status: "silver" },
];
customers.filter(isHighValueCustomer);
You can also use $not, $regex, $size, $elemMatch, and any other Mango operator:
const containsLargeAttachment = drupe({
attachments: {
$elemMatch: {
content_type: { $regex: "^image/" },
length: { $gt: 1_000_000 },
},
},
});
docs.filter(containsLargeAttachment);
API
const predicate = drupe(selector);
predicate(document);
selector – Any valid Mango selector object.
- Returns a predicate function that can be reused across collections, iterables, or bespoke logic.
TypeScript: The exported signature is intentionally loose—(selector: any) => (subject: unknown) => boolean—so you can refine the shape that makes sense for your app.
How it works
drupe is intentionally tiny:
import { matchesSelector } from "pouchdb-selector-core";
export const drupe = (selector: any) => (subject: unknown) => {
return matchesSelector(subject, selector);
};
When you call drupe(selector), it defers entirely to matchesSelector, the same function PouchDB uses internally to evaluate Mango selectors. That means:
- Behaviour matches CouchDB/PouchDB exactly (including quirks and edge cases).
- New Mango features arrive as soon as they land in
pouchdb-selector-core.
- There is no custom query engine to maintain.
Related resources
License
MIT © Sam Holmes