Skip to content

EdgeApp/drupe

Repository files navigation

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:

# From NPM
yarn add @edge.app/drupe

# From JSR
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);
// -> [{ 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); // -> boolean
  • 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

About

A TypeScript library for mango predicates

Resources

Stars

Watchers

Forks

Packages

No packages published