@edge.app/drupe
v0.1.0
Published
> Tiny helper that lets you run [Mango selectors](https://docs.couchdb.org/en/stable/api/database/find.html#selector-syntax) anywhere you need a JavaScript predicate.
Keywords
Readme
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
matchesSelectorimplementation. - Zero configuration, fully typed, and framework/runtime agnostic.
Installation
Install from JSR:
# From NPM
yarn add @edge.app/drupe
# From JSR
npx jsr add @edgeapp/drupeQuick 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);
// -> [{ type: 'article', slug: 'mango-101', tags: ['databases'], publishedAt: '2024-05-02' }]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);
// -> [{ name: 'Ada', spend: 3200, status: 'platinum' }]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); // -> booleanselector– 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
- CouchDB Mango selector reference: https://docs.couchdb.org/en/stable/api/database/find.html
- PouchDB Mango query guide: https://pouchdb.com/guides/mango-queries.html
- Inspiration:
siftlets you filter arrays with MongoDB selectors—check it out at https://github.com/crcn/sift.js
License
MIT © Sam Holmes
